1 /*******************************************************************************
2  * Copyright (c) 2000, 2018 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Andrey Loskutov <loskutov@gmx.de> - bug 488172
14  *     Stefan Xenos (Google) - bug 487254 - StyledText.getTopIndex() can return negative values
15  *     Angelo Zerr <angelo.zerr@gmail.com> - Customize different line spacing of StyledText - Bug 522020
16  *     Karsten Thoms <thoms@itemis.de> - bug 528746 add getOffsetAtPoint(Point)
17  *******************************************************************************/
18 package org.eclipse.swt.custom;
19 
20 
21 import java.util.*;
22 import java.util.List;
23 
24 import org.eclipse.swt.*;
25 import org.eclipse.swt.accessibility.*;
26 import org.eclipse.swt.dnd.*;
27 import org.eclipse.swt.events.*;
28 import org.eclipse.swt.graphics.*;
29 import org.eclipse.swt.internal.*;
30 import org.eclipse.swt.printing.*;
31 import org.eclipse.swt.widgets.*;
32 
33 /**
34  * A StyledText is an editable user interface object that displays lines
35  * of text.  The following style attributes can be defined for the text:
36  * <ul>
37  * <li>foreground color
38  * <li>background color
39  * <li>font style (bold, italic, bold-italic, regular)
40  * <li>underline
41  * <li>strikeout
42  * </ul>
43  * <p>
44  * In addition to text style attributes, the background color of a line may
45  * be specified.
46  * </p><p>
47  * There are two ways to use this widget when specifying text style information.
48  * You may use the API that is defined for StyledText or you may define your own
49  * LineStyleListener.  If you define your own listener, you will be responsible
50  * for maintaining the text style information for the widget.  IMPORTANT: You may
51  * not define your own listener and use the StyledText API.  The following
52  * StyledText API is not supported if you have defined a LineStyleListener:</p>
53  * <ul>
54  * <li>getStyleRangeAtOffset(int)
55  * <li>getStyleRanges()
56  * <li>replaceStyleRanges(int,int,StyleRange[])
57  * <li>setStyleRange(StyleRange)
58  * <li>setStyleRanges(StyleRange[])
59  * </ul>
60  * <p>
61  * There are two ways to use this widget when specifying line background colors.
62  * You may use the API that is defined for StyledText or you may define your own
63  * LineBackgroundListener.  If you define your own listener, you will be responsible
64  * for maintaining the line background color information for the widget.
65  * IMPORTANT: You may not define your own listener and use the StyledText API.
66  * The following StyledText API is not supported if you have defined a
67  * LineBackgroundListener:</p>
68  * <ul>
69  * <li>getLineBackground(int)
70  * <li>setLineBackground(int,int,Color)
71  * </ul>
72  * <p>
73  * The content implementation for this widget may also be user-defined.  To do so,
74  * you must implement the StyledTextContent interface and use the StyledText API
75  * setContent(StyledTextContent) to initialize the widget.
76  * </p>
77  * <dl>
78  * <dt><b>Styles:</b><dd>FULL_SELECTION, MULTI, READ_ONLY, SINGLE, WRAP
79  * <dt><b>Events:</b><dd>ExtendedModify, LineGetBackground, LineGetSegments, LineGetStyle, Modify, Selection, Verify, VerifyKey, OrientationChange
80  * </dl>
81  * <p>
82  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
83  * </p>
84  *
85  * @see <a href="http://www.eclipse.org/swt/snippets/#styledtext">StyledText snippets</a>
86  * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Examples: CustomControlExample, TextEditor</a>
87  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
88  * @noextend This class is not intended to be subclassed by clients.
89  */
90 public class StyledText extends Canvas {
91 	static final char TAB = '\t';
92 	static final String PlatformLineDelimiter = System.lineSeparator();
93 	static final int BIDI_CARET_WIDTH = 3;
94 	static final int DEFAULT_WIDTH	= 64;
95 	static final int DEFAULT_HEIGHT = 64;
96 	static final int V_SCROLL_RATE = 50;
97 	static final int H_SCROLL_RATE = 10;
98 	static final int PREVIOUS_OFFSET_TRAILING = 0;
99 	static final int OFFSET_LEADING = 1;
100 
101 	static final String STYLEDTEXT_KEY = "org.eclipse.swt.internal.cocoa.styledtext"; //$NON-NLS-1$
102 
103 	Color selectionBackground;	// selection background color
104 	Color selectionForeground;	// selection foreground color
105 	StyledTextContent content;			// native content (default or user specified)
106 	StyledTextRenderer renderer;
107 	Listener listener;
108 	TextChangeListener textChangeListener;	// listener for TextChanging, TextChanged and TextSet events from StyledTextContent
109 	int verticalScrollOffset = 0;		// pixel based
110 	int horizontalScrollOffset = 0;		// pixel based
111 	boolean alwaysShowScroll = true;
112 	int ignoreResize = 0;
113 	int topIndex = 0;					// top visible line
114 	int topIndexY;
115 	int clientAreaHeight = 0;			// the client area height. Needed to calculate content width for new visible lines during Resize callback
116 	int clientAreaWidth = 0;			// the client area width. Needed during Resize callback to determine if line wrap needs to be recalculated
117 	int tabLength = 4;					// number of characters in a tab
118 	int [] tabs;
119 	int leftMargin;
120 	int topMargin;
121 	int rightMargin;
122 	int bottomMargin;
123 	Color marginColor;
124 	int columnX;						// keep track of the horizontal caret position when changing lines/pages. Fixes bug 5935
125 	int caretOffset;
126 	int caretAlignment;
127 	Point selection = new Point(0, 0);	// x and y are start and end caret offsets of selection (x <= y)
128 	Point clipboardSelection;           // x and y are start and end caret offsets of previous selection
129 	int selectionAnchor;				// position of selection anchor. 0 based offset from beginning of text
130 	Point doubleClickSelection;			// selection after last mouse double click
131 	boolean editable = true;
132 	boolean wordWrap = false;			// text is wrapped automatically
133 	boolean visualWrap = false;		// process line breaks inside logical lines (inserted by BidiSegmentEvent)
134 	boolean hasStyleWithVariableHeight = false;
135 	boolean hasVerticalIndent = false;
136 	boolean doubleClickEnabled = true;	// see getDoubleClickEnabled
137 	boolean overwrite = false;			// insert/overwrite edit mode
138 	int textLimit = -1;					// limits the number of characters the user can type in the widget. Unlimited by default.
139 	Map<Integer, Integer> keyActionMap = new HashMap<>();
140 	Color background = null;			// workaround for bug 4791
141 	Color foreground = null;			//
142 	/** True if a non-default background color is set */
143 	boolean customBackground;
144 	/** True if a non-default foreground color is set */
145 	boolean customForeground;
146 	/** False iff the widget is disabled */
147 	boolean enabled = true;
148 	/** True iff the widget is in the midst of being enabled or disabled */
149 	boolean insideSetEnableCall;
150 	Clipboard clipboard;
151 	int clickCount;
152 	int autoScrollDirection = SWT.NULL;	// the direction of autoscrolling (up, down, right, left)
153 	int autoScrollDistance = 0;
154 	int lastTextChangeStart;			// cache data of the
155 	int lastTextChangeNewLineCount;		// last text changing
156 	int lastTextChangeNewCharCount;		// event for use in the
157 	int lastTextChangeReplaceLineCount;	// text changed handler
158 	int lastTextChangeReplaceCharCount;
159 	int lastCharCount = 0;
160 	int lastLineBottom;					// the bottom pixel of the last line been replaced
161 	boolean bidiColoring = false;		// apply the BIDI algorithm on text segments of the same color
162 	Image leftCaretBitmap = null;
163 	Image rightCaretBitmap = null;
164 	int caretDirection = SWT.NULL;
165 	int caretWidth = 0;
166 	Caret defaultCaret = null;
167 	boolean updateCaretDirection = true;
168 	boolean dragDetect = true;
169 	IME ime;
170 	Cursor cursor;
171 	int alignment;
172 	boolean justify;
173 	int indent, wrapIndent;
174 	int lineSpacing;
175 	int alignmentMargin;
176 	int newOrientation = SWT.NONE;
177 	int accCaretOffset;
178 	Accessible acc;
179 	AccessibleControlAdapter accControlAdapter;
180 	AccessibleAttributeAdapter accAttributeAdapter;
181 	AccessibleEditableTextListener accEditableTextListener;
182 	AccessibleTextExtendedAdapter accTextExtendedAdapter;
183 	AccessibleAdapter accAdapter;
184 	MouseNavigator mouseNavigator;
185 	boolean middleClickPressed;
186 
187 	//block selection
188 	boolean blockSelection;
189 	int blockXAnchor = -1, blockYAnchor = -1;
190 	int blockXLocation = -1, blockYLocation = -1;
191 
192 	final static boolean IS_MAC, IS_GTK;
193 	static {
194 		String platform = SWT.getPlatform();
195 		IS_MAC = "cocoa".equals(platform);
196 		IS_GTK = "gtk".equals(platform);
197 	}
198 
199 	/**
200 	 * The Printing class implements printing of a range of text.
201 	 * An instance of <code>Printing</code> is returned in the
202 	 * StyledText#print(Printer) API. The run() method may be
203 	 * invoked from any thread.
204 	 */
205 	static class Printing implements Runnable {
206 		final static int LEFT = 0;						// left aligned header/footer segment
207 		final static int CENTER = 1;					// centered header/footer segment
208 		final static int RIGHT = 2;						// right aligned header/footer segment
209 
210 		Printer printer;
211 		StyledTextRenderer printerRenderer;
212 		StyledTextPrintOptions printOptions;
213 		Rectangle clientArea;
214 		FontData fontData;
215 		Font printerFont;
216 		Map<Resource, Resource> resources;
217 		int tabLength;
218 		GC gc;											// printer GC
219 		int pageWidth;									// width of a printer page in pixels
220 		int startPage;									// first page to print
221 		int endPage;									// last page to print
222 		int scope;										// scope of print job
223 		int startLine;									// first (wrapped) line to print
224 		int endLine;									// last (wrapped) line to print
225 		boolean singleLine;								// widget single line mode
226 		Point selection = null;					// selected text
227 		boolean mirrored;						// indicates the printing gc should be mirrored
228 		int lineSpacing;
229 		int printMargin;
230 
231 	/**
232 	 * Creates an instance of <code>Printing</code>.
233 	 * Copies the widget content and rendering data that needs
234 	 * to be requested from listeners.
235 	 *
236 	 * @param parent StyledText widget to print.
237 	 * @param printer printer device to print on.
238 	 * @param printOptions print options
239 	 */
Printing(StyledText styledText, Printer printer, StyledTextPrintOptions printOptions)240 	Printing(StyledText styledText, Printer printer, StyledTextPrintOptions printOptions) {
241 		this.printer = printer;
242 		this.printOptions = printOptions;
243 		this.mirrored = (styledText.getStyle() & SWT.MIRRORED) != 0;
244 		singleLine = styledText.isSingleLine();
245 		startPage = 1;
246 		endPage = Integer.MAX_VALUE;
247 		PrinterData data = printer.getPrinterData();
248 		scope = data.scope;
249 		if (scope == PrinterData.PAGE_RANGE) {
250 			startPage = data.startPage;
251 			endPage = data.endPage;
252 			if (endPage < startPage) {
253 				int temp = endPage;
254 				endPage = startPage;
255 				startPage = temp;
256 			}
257 		} else if (scope == PrinterData.SELECTION) {
258 			selection = styledText.getSelectionRange();
259 		}
260 		printerRenderer = new StyledTextRenderer(printer, null);
261 		printerRenderer.setContent(copyContent(styledText.getContent()));
262 		cacheLineData(styledText);
263 	}
264 	/**
265 	 * Caches all line data that needs to be requested from a listener.
266 	 *
267 	 * @param printerContent <code>StyledTextContent</code> to request
268 	 * 	line data for.
269 	 */
cacheLineData(StyledText styledText)270 	void cacheLineData(StyledText styledText) {
271 		StyledTextRenderer renderer = styledText.renderer;
272 		renderer.copyInto(printerRenderer);
273 		fontData = styledText.getFont().getFontData()[0];
274 		tabLength = styledText.tabLength;
275 		int lineCount = printerRenderer.lineCount;
276 		if (styledText.isListening(ST.LineGetBackground) || (styledText.isListening(ST.LineGetSegments)) || styledText.isListening(ST.LineGetStyle)) {
277 			StyledTextContent content = printerRenderer.content;
278 			for (int i = 0; i < lineCount; i++) {
279 				String line = content.getLine(i);
280 				int lineOffset = content.getOffsetAtLine(i);
281 				StyledTextEvent event = styledText.getLineBackgroundData(lineOffset, line);
282 				if (event != null && event.lineBackground != null) {
283 					printerRenderer.setLineBackground(i, 1, event.lineBackground);
284 				}
285 				event = styledText.getBidiSegments(lineOffset, line);
286 				if (event != null) {
287 					printerRenderer.setLineSegments(i, 1, event.segments);
288 					printerRenderer.setLineSegmentChars(i, 1, event.segmentsChars);
289 				}
290 				event = styledText.getLineStyleData(lineOffset, line);
291 				if (event != null) {
292 					printerRenderer.setLineIndent(i, 1, event.indent);
293 					printerRenderer.setLineAlignment(i, 1, event.alignment);
294 					printerRenderer.setLineJustify(i, 1, event.justify);
295 					printerRenderer.setLineBullet(i, 1, event.bullet);
296 					StyleRange[] styles = event.styles;
297 					if (styles != null && styles.length > 0) {
298 						printerRenderer.setStyleRanges(event.ranges, styles);
299 					}
300 				}
301 			}
302 		}
303 		Point screenDPI = styledText.getDisplay().getDPI();
304 		Point printerDPI = printer.getDPI();
305 		resources = new HashMap<> ();
306 		for (int i = 0; i < lineCount; i++) {
307 			Color color = printerRenderer.getLineBackground(i, null);
308 			if (color != null) {
309 				if (printOptions.printLineBackground) {
310 					Color printerColor = (Color)resources.get(color);
311 					if (printerColor == null) {
312 						printerColor = new Color (printer, color.getRGB());
313 						resources.put(color, printerColor);
314 					}
315 					printerRenderer.setLineBackground(i, 1, printerColor);
316 				} else {
317 					printerRenderer.setLineBackground(i, 1, null);
318 				}
319 			}
320 			int indent = printerRenderer.getLineIndent(i, 0);
321 			if (indent != 0) {
322 				printerRenderer.setLineIndent(i, 1, indent * printerDPI.x / screenDPI.x);
323 			}
324 		}
325 		StyleRange[] styles = printerRenderer.styles;
326 		for (int i = 0; i < printerRenderer.styleCount; i++) {
327 			StyleRange style = styles[i];
328 			Font font = style.font;
329 			if (style.font != null) {
330 				Font printerFont = (Font)resources.get(font);
331 				if (printerFont == null) {
332 					printerFont = new Font (printer, font.getFontData());
333 					resources.put(font, printerFont);
334 				}
335 				style.font = printerFont;
336 			}
337 			Color color = style.foreground;
338 			if (color != null) {
339 				Color printerColor = (Color)resources.get(color);
340 				if (printOptions.printTextForeground) {
341 					if (printerColor == null) {
342 						printerColor = new Color (printer, color.getRGB());
343 						resources.put(color, printerColor);
344 					}
345 					style.foreground = printerColor;
346 				} else {
347 					style.foreground = null;
348 				}
349 			}
350 			color = style.background;
351 			if (color != null) {
352 				Color printerColor = (Color)resources.get(color);
353 				if (printOptions.printTextBackground) {
354 					if (printerColor == null) {
355 						printerColor = new Color (printer, color.getRGB());
356 						resources.put(color, printerColor);
357 					}
358 					style.background = printerColor;
359 				} else {
360 					style.background = null;
361 				}
362 			}
363 			if (!printOptions.printTextFontStyle) {
364 				style.fontStyle = SWT.NORMAL;
365 			}
366 			style.rise = style.rise * printerDPI.y / screenDPI.y;
367 			GlyphMetrics metrics = style.metrics;
368 			if (metrics != null) {
369 				metrics.ascent = metrics.ascent * printerDPI.y / screenDPI.y;
370 				metrics.descent = metrics.descent * printerDPI.y / screenDPI.y;
371 				metrics.width = metrics.width * printerDPI.x / screenDPI.x;
372 			}
373 		}
374 		lineSpacing = styledText.lineSpacing * printerDPI.y / screenDPI.y;
375 		if (printOptions.printLineNumbers) {
376 			printMargin = 3 * printerDPI.x / screenDPI.x;
377 		}
378 	}
379 	/**
380 	 * Copies the text of the specified <code>StyledTextContent</code>.
381 	 *
382 	 * @param original the <code>StyledTextContent</code> to copy.
383 	 */
copyContent(StyledTextContent original)384 	StyledTextContent copyContent(StyledTextContent original) {
385 		StyledTextContent printerContent = new DefaultContent();
386 		int insertOffset = 0;
387 		for (int i = 0; i < original.getLineCount(); i++) {
388 			int insertEndOffset;
389 			if (i < original.getLineCount() - 1) {
390 				insertEndOffset = original.getOffsetAtLine(i + 1);
391 			} else {
392 				insertEndOffset = original.getCharCount();
393 			}
394 			printerContent.replaceTextRange(insertOffset, 0, original.getTextRange(insertOffset, insertEndOffset - insertOffset));
395 			insertOffset = insertEndOffset;
396 		}
397 		return printerContent;
398 	}
399 	/**
400 	 * Disposes of the resources and the <code>PrintRenderer</code>.
401 	 */
dispose()402 	void dispose() {
403 		if (gc != null) {
404 			gc.dispose();
405 			gc = null;
406 		}
407 		if (resources != null) {
408 			for (Resource resource : resources.values()) {
409 				resource.dispose();
410 			}
411 			resources = null;
412 		}
413 		if (printerFont != null) {
414 			printerFont.dispose();
415 			printerFont = null;
416 		}
417 		if (printerRenderer != null) {
418 			printerRenderer.dispose();
419 			printerRenderer = null;
420 		}
421 	}
init()422 	void init() {
423 		Rectangle trim = printer.computeTrim(0, 0, 0, 0);
424 		Point dpi = printer.getDPI();
425 
426 		printerFont = new Font(printer, fontData.getName(), fontData.getHeight(), SWT.NORMAL);
427 		clientArea = printer.getClientArea();
428 		pageWidth = clientArea.width;
429 		// one inch margin around text
430 		clientArea.x = dpi.x + trim.x;
431 		clientArea.y = dpi.y + trim.y;
432 		clientArea.width -= (clientArea.x + trim.width);
433 		clientArea.height -= (clientArea.y + trim.height);
434 
435 		int style = mirrored ? SWT.RIGHT_TO_LEFT : SWT.LEFT_TO_RIGHT;
436 		gc = new GC(printer, style);
437 		gc.setFont(printerFont);
438 		printerRenderer.setFont(printerFont, tabLength);
439 		int lineHeight = printerRenderer.getLineHeight();
440 		if (printOptions.header != null) {
441 			clientArea.y += lineHeight * 2;
442 			clientArea.height -= lineHeight * 2;
443 		}
444 		if (printOptions.footer != null) {
445 			clientArea.height -= lineHeight * 2;
446 		}
447 
448 		// TODO not wrapped
449 		StyledTextContent content = printerRenderer.content;
450 		startLine = 0;
451 		endLine = singleLine ? 0 : content.getLineCount() - 1;
452 		if (scope == PrinterData.PAGE_RANGE) {
453 			int pageSize = clientArea.height / lineHeight;//WRONG
454 			startLine = (startPage - 1) * pageSize;
455 		} else if (scope == PrinterData.SELECTION) {
456 			startLine = content.getLineAtOffset(selection.x);
457 			if (selection.y > 0) {
458 				endLine = content.getLineAtOffset(selection.x + selection.y - 1);
459 			} else {
460 				endLine = startLine - 1;
461 			}
462 		}
463 	}
464 	/**
465 	 * Prints the lines in the specified page range.
466 	 */
print()467 	void print() {
468 		Color background = gc.getBackground();
469 		Color foreground = gc.getForeground();
470 		int paintY = clientArea.y;
471 		int paintX = clientArea.x;
472 		int width = clientArea.width;
473 		int page = startPage;
474 		int pageBottom = clientArea.y + clientArea.height;
475 		int orientation =  gc.getStyle() & (SWT.RIGHT_TO_LEFT | SWT.LEFT_TO_RIGHT);
476 		TextLayout printLayout = null;
477 		if (printOptions.printLineNumbers || printOptions.header != null || printOptions.footer != null) {
478 			printLayout = new TextLayout(printer);
479 			printLayout.setFont(printerFont);
480 		}
481 		if (printOptions.printLineNumbers) {
482 			int numberingWidth = 0;
483 			int count = endLine - startLine + 1;
484 			String[] lineLabels = printOptions.lineLabels;
485 			if (lineLabels != null) {
486 				for (int i = startLine; i < Math.min(count, lineLabels.length); i++) {
487 					if (lineLabels[i] != null) {
488 						printLayout.setText(lineLabels[i]);
489 						int lineWidth = printLayout.getBounds().width;
490 						numberingWidth = Math.max(numberingWidth, lineWidth);
491 					}
492 				}
493 			} else {
494 				StringBuilder buffer = new StringBuilder("0");
495 				while ((count /= 10) > 0) buffer.append("0");
496 				printLayout.setText(buffer.toString());
497 				numberingWidth = printLayout.getBounds().width;
498 			}
499 			numberingWidth += printMargin;
500 			if (numberingWidth > width) numberingWidth = width;
501 			paintX += numberingWidth;
502 			width -= numberingWidth;
503 		}
504 		for (int i = startLine; i <= endLine && page <= endPage; i++) {
505 			if (paintY == clientArea.y) {
506 				printer.startPage();
507 				printDecoration(page, true, printLayout);
508 			}
509 			TextLayout layout = printerRenderer.getTextLayout(i, orientation, width, lineSpacing);
510 			Color lineBackground = printerRenderer.getLineBackground(i, background);
511 			int paragraphBottom = paintY + layout.getBounds().height;
512 			if (paragraphBottom <= pageBottom) {
513 				//normal case, the whole paragraph fits in the current page
514 				printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
515 				paintY = paragraphBottom;
516 			} else {
517 				int lineCount = layout.getLineCount();
518 				while (paragraphBottom > pageBottom && lineCount > 0) {
519 					lineCount--;
520 					paragraphBottom -= layout.getLineBounds(lineCount).height + layout.getSpacing();
521 				}
522 				if (lineCount == 0) {
523 					//the whole paragraph goes to the next page
524 					printDecoration(page, false, printLayout);
525 					printer.endPage();
526 					page++;
527 					if (page <= endPage) {
528 						printer.startPage();
529 						printDecoration(page, true, printLayout);
530 						paintY = clientArea.y;
531 						printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
532 						paintY += layout.getBounds().height;
533 					}
534 				} else {
535 					//draw paragraph top in the current page and paragraph bottom in the next
536 					int height = paragraphBottom - paintY;
537 					gc.setClipping(clientArea.x, paintY, clientArea.width, height);
538 					printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
539 					gc.setClipping((Rectangle)null);
540 					printDecoration(page, false, printLayout);
541 					printer.endPage();
542 					page++;
543 					if (page <= endPage) {
544 						printer.startPage();
545 						printDecoration(page, true, printLayout);
546 						paintY = clientArea.y - height;
547 						int layoutHeight = layout.getBounds().height;
548 						gc.setClipping(clientArea.x, clientArea.y, clientArea.width, layoutHeight - height);
549 						printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
550 						gc.setClipping((Rectangle)null);
551 						paintY += layoutHeight;
552 					}
553 				}
554 			}
555 			printerRenderer.disposeTextLayout(layout);
556 		}
557 		if (page <= endPage && paintY > clientArea.y) {
558 			// close partial page
559 			printDecoration(page, false, printLayout);
560 			printer.endPage();
561 		}
562 		if (printLayout != null) printLayout.dispose();
563 	}
564 	/**
565 	 * Print header or footer decorations.
566 	 *
567 	 * @param page page number to print, if specified in the StyledTextPrintOptions header or footer.
568 	 * @param header true = print the header, false = print the footer
569 	 */
printDecoration(int page, boolean header, TextLayout layout)570 	void printDecoration(int page, boolean header, TextLayout layout) {
571 		String text = header ? printOptions.header : printOptions.footer;
572 		if (text == null) return;
573 		int lastSegmentIndex = 0;
574 		for (int i = 0; i < 3; i++) {
575 			int segmentIndex = text.indexOf(StyledTextPrintOptions.SEPARATOR, lastSegmentIndex);
576 			String segment;
577 			if (segmentIndex == -1) {
578 				segment = text.substring(lastSegmentIndex);
579 				printDecorationSegment(segment, i, page, header, layout);
580 				break;
581 			} else {
582 				segment = text.substring(lastSegmentIndex, segmentIndex);
583 				printDecorationSegment(segment, i, page, header, layout);
584 				lastSegmentIndex = segmentIndex + StyledTextPrintOptions.SEPARATOR.length();
585 			}
586 		}
587 	}
588 	/**
589 	 * Print one segment of a header or footer decoration.
590 	 * Headers and footers have three different segments.
591 	 * One each for left aligned, centered, and right aligned text.
592 	 *
593 	 * @param segment decoration segment to print
594 	 * @param alignment alignment of the segment. 0=left, 1=center, 2=right
595 	 * @param page page number to print, if specified in the decoration segment.
596 	 * @param header true = print the header, false = print the footer
597 	 */
printDecorationSegment(String segment, int alignment, int page, boolean header, TextLayout layout)598 	void printDecorationSegment(String segment, int alignment, int page, boolean header, TextLayout layout) {
599 		int pageIndex = segment.indexOf(StyledTextPrintOptions.PAGE_TAG);
600 		if (pageIndex != -1) {
601 			int pageTagLength = StyledTextPrintOptions.PAGE_TAG.length();
602 			StringBuilder buffer = new StringBuilder(segment.substring (0, pageIndex));
603 			buffer.append (page);
604 			buffer.append (segment.substring(pageIndex + pageTagLength));
605 			segment = buffer.toString();
606 		}
607 		if (segment.length() > 0) {
608 			layout.setText(segment);
609 			int segmentWidth = layout.getBounds().width;
610 			int segmentHeight = printerRenderer.getLineHeight();
611 			int drawX = 0, drawY;
612 			if (alignment == LEFT) {
613 				drawX = clientArea.x;
614 			} else if (alignment == CENTER) {
615 				drawX = (pageWidth - segmentWidth) / 2;
616 			} else if (alignment == RIGHT) {
617 				drawX = clientArea.x + clientArea.width - segmentWidth;
618 			}
619 			if (header) {
620 				drawY = clientArea.y - segmentHeight * 2;
621 			} else {
622 				drawY = clientArea.y + clientArea.height + segmentHeight;
623 			}
624 			layout.draw(gc, drawX, drawY);
625 		}
626 	}
printLine(int x, int y, GC gc, Color foreground, Color background, TextLayout layout, TextLayout printLayout, int index)627 	void printLine(int x, int y, GC gc, Color foreground, Color background, TextLayout layout, TextLayout printLayout, int index) {
628 		if (background != null) {
629 			Rectangle rect = layout.getBounds();
630 			gc.setBackground(background);
631 			gc.fillRectangle(x, y, rect.width, rect.height);
632 
633 //			int lineCount = layout.getLineCount();
634 //			for (int i = 0; i < lineCount; i++) {
635 //				Rectangle rect = layout.getLineBounds(i);
636 //				rect.x += paintX;
637 //				rect.y += paintY + layout.getSpacing();
638 //				rect.width = width;//layout bounds
639 //				gc.fillRectangle(rect);
640 //			}
641 		}
642 		if (printOptions.printLineNumbers) {
643 			FontMetrics metrics = layout.getLineMetrics(0);
644 			printLayout.setAscent(metrics.getAscent() + metrics.getLeading());
645 			printLayout.setDescent(metrics.getDescent());
646 			String[] lineLabels = printOptions.lineLabels;
647 			if (lineLabels != null) {
648 				if (0 <= index && index < lineLabels.length && lineLabels[index] != null) {
649 					printLayout.setText(lineLabels[index]);
650 				} else {
651 					printLayout.setText("");
652 				}
653 			} else {
654 				printLayout.setText(String.valueOf(index));
655 			}
656 			int paintX = x - printMargin - printLayout.getBounds().width;
657 			printLayout.draw(gc, paintX, y);
658 			printLayout.setAscent(-1);
659 			printLayout.setDescent(-1);
660 		}
661 		gc.setForeground(foreground);
662 		layout.draw(gc, x, y);
663 	}
664 	/**
665 	 * Starts a print job and prints the pages specified in the constructor.
666 	 */
667 	@Override
run()668 	public void run() {
669 		String jobName = printOptions.jobName;
670 		if (jobName == null) {
671 			jobName = "Printing";
672 		}
673 		if (printer.startJob(jobName)) {
674 			init();
675 			print();
676 			dispose();
677 			printer.endJob();
678 		}
679 	}
680 	}
681 	/**
682 	 * The <code>RTFWriter</code> class is used to write widget content as
683 	 * rich text. The implementation complies with the RTF specification
684 	 * version 1.5.
685 	 * <p>
686 	 * toString() is guaranteed to return a valid RTF string only after
687 	 * close() has been called.
688 	 * </p><p>
689 	 * Whole and partial lines and line breaks can be written. Lines will be
690 	 * formatted using the styles queried from the LineStyleListener, if
691 	 * set, or those set directly in the widget. All styles are applied to
692 	 * the RTF stream like they are rendered by the widget. In addition, the
693 	 * widget font name and size is used for the whole text.
694 	 * </p>
695 	 */
696 	class RTFWriter extends TextWriter {
697 		static final int DEFAULT_FOREGROUND = 0;
698 		static final int DEFAULT_BACKGROUND = 1;
699 		List<Color> colorTable;
700 		List<Font> fontTable;
701 
702 	/**
703 	 * Creates a RTF writer that writes content starting at offset "start"
704 	 * in the document.  <code>start</code> and <code>length</code>can be set to specify partial
705 	 * lines.
706 	 *
707 	 * @param start start offset of content to write, 0 based from
708 	 * 	beginning of document
709 	 * @param length length of content to write
710 	 */
RTFWriter(int start, int length)711 	public RTFWriter(int start, int length) {
712 		super(start, length);
713 		colorTable = new ArrayList<>();
714 		fontTable = new ArrayList<>();
715 		colorTable.add(getForeground());
716 		colorTable.add(getBackground());
717 		fontTable.add(getFont());
718 	}
719 	/**
720 	 * Closes the RTF writer. Once closed no more content can be written.
721 	 * <b>NOTE:</b>  <code>toString()</code> does not return a valid RTF string until
722 	 * <code>close()</code> has been called.
723 	 */
724 	@Override
close()725 	public void close() {
726 		if (!isClosed()) {
727 			writeHeader();
728 			write("\n}}\0");
729 			super.close();
730 		}
731 	}
732 	/**
733 	 * Returns the index of the specified color in the RTF color table.
734 	 *
735 	 * @param color the color
736 	 * @param defaultIndex return value if color is null
737 	 * @return the index of the specified color in the RTF color table
738 	 * 	or "defaultIndex" if "color" is null.
739 	 */
getColorIndex(Color color, int defaultIndex)740 	int getColorIndex(Color color, int defaultIndex) {
741 		if (color == null) return defaultIndex;
742 		int index = colorTable.indexOf(color);
743 		if (index == -1) {
744 			index = colorTable.size();
745 			colorTable.add(color);
746 		}
747 		return index;
748 	}
749 	/**
750 	 * Returns the index of the specified color in the RTF color table.
751 	 *
752 	 * @param color the color
753 	 * @param defaultIndex return value if color is null
754 	 * @return the index of the specified color in the RTF color table
755 	 * 	or "defaultIndex" if "color" is null.
756 	 */
getFontIndex(Font font)757 	int getFontIndex(Font font) {
758 		int index = fontTable.indexOf(font);
759 		if (index == -1) {
760 			index = fontTable.size();
761 			fontTable.add(font);
762 		}
763 		return index;
764 	}
765 	/**
766 	 * Appends the specified segment of "string" to the RTF data.
767 	 * Copy from <code>start</code> up to, but excluding, <code>end</code>.
768 	 *
769 	 * @param string string to copy a segment from. Must not contain
770 	 * 	line breaks. Line breaks should be written using writeLineDelimiter()
771 	 * @param start start offset of segment. 0 based.
772 	 * @param end end offset of segment
773 	 */
write(String string, int start, int end)774 	void write(String string, int start, int end) {
775 		for (int index = start; index < end; index++) {
776 			char ch = string.charAt(index);
777 			if (ch > 0x7F) {
778 				// write the sub string from the last escaped character
779 				// to the current one. Fixes bug 21698.
780 				if (index > start) {
781 					write(string.substring(start, index));
782 				}
783 				write("\\u");
784 				write(Integer.toString((short) ch));
785 				write('?');						// ANSI representation (1 byte long, \\uc1)
786 				start = index + 1;
787 			} else if (ch == '}' || ch == '{' || ch == '\\') {
788 				// write the sub string from the last escaped character
789 				// to the current one. Fixes bug 21698.
790 				if (index > start) {
791 					write(string.substring(start, index));
792 				}
793 				write('\\');
794 				write(ch);
795 				start = index + 1;
796 			}
797 		}
798 		// write from the last escaped character to the end.
799 		// Fixes bug 21698.
800 		if (start < end) {
801 			write(string.substring(start, end));
802 		}
803 	}
804 	/**
805 	 * Writes the RTF header including font table and color table.
806 	 */
writeHeader()807 	void writeHeader() {
808 		StringBuilder header = new StringBuilder();
809 		FontData fontData = getFont().getFontData()[0];
810 		header.append("{\\rtf1\\ansi");
811 		// specify code page, necessary for copy to work in bidi
812 		// systems that don't support Unicode RTF.
813 		String cpg = System.getProperty("file.encoding").toLowerCase();
814 		if (cpg.startsWith("cp") || cpg.startsWith("ms")) {
815 			cpg = cpg.substring(2, cpg.length());
816 			header.append("\\ansicpg");
817 			header.append(cpg);
818 		}
819 		header.append("\\uc1\\deff0{\\fonttbl{\\f0\\fnil ");
820 		header.append(fontData.getName());
821 		header.append(";");
822 		for (int i = 1; i < fontTable.size(); i++) {
823 			header.append("\\f");
824 			header.append(i);
825 			header.append(" ");
826 			FontData fd = fontTable.get(i).getFontData()[0];
827 			header.append(fd.getName());
828 			header.append(";");
829 		}
830 		header.append("}}\n{\\colortbl");
831 		for (Color color : colorTable) {
832 			header.append("\\red");
833 			header.append(color.getRed());
834 			header.append("\\green");
835 			header.append(color.getGreen());
836 			header.append("\\blue");
837 			header.append(color.getBlue());
838 			header.append(";");
839 		}
840 		// some RTF readers ignore the deff0 font tag. Explicitly
841 		// set the font for the whole document to work around this.
842 		header.append("}\n{\\f0\\fs");
843 		// font size is specified in half points
844 		header.append(fontData.getHeight() * 2);
845 		header.append(" ");
846 		write(header.toString(), 0);
847 	}
848 	/**
849 	 * Appends the specified line text to the RTF data.  Lines will be formatted
850 	 * using the styles queried from the LineStyleListener, if set, or those set
851 	 * directly in the widget.
852 	 *
853 	 * @param line line text to write as RTF. Must not contain line breaks
854 	 * 	Line breaks should be written using writeLineDelimiter()
855 	 * @param lineOffset offset of the line. 0 based from the start of the
856 	 * 	widget document. Any text occurring before the start offset or after the
857 	 * 	end offset specified during object creation is ignored.
858 	 * @exception SWTException <ul>
859 	 *   <li>ERROR_IO when the writer is closed.</li>
860 	 * </ul>
861 	 */
862 	@Override
writeLine(String line, int lineOffset)863 	public void writeLine(String line, int lineOffset) {
864 		if (isClosed()) {
865 			SWT.error(SWT.ERROR_IO);
866 		}
867 		int lineIndex = content.getLineAtOffset(lineOffset);
868 		int lineAlignment, lineIndent;
869 		boolean lineJustify;
870 		int[] ranges;
871 		StyleRange[] styles;
872 		StyledTextEvent event = getLineStyleData(lineOffset, line);
873 		if (event != null) {
874 			lineAlignment = event.alignment;
875 			lineIndent = event.indent;
876 			lineJustify = event.justify;
877 			ranges = event.ranges;
878 			styles = event.styles;
879 		} else {
880 			lineAlignment = renderer.getLineAlignment(lineIndex, alignment);
881 			lineIndent =  renderer.getLineIndent(lineIndex, indent);
882 			lineJustify = renderer.getLineJustify(lineIndex, justify);
883 			ranges = renderer.getRanges(lineOffset, line.length());
884 			styles = renderer.getStyleRanges(lineOffset, line.length(), false);
885 		}
886 		if (styles == null) styles = new StyleRange[0];
887 		Color lineBackground = renderer.getLineBackground(lineIndex, null);
888 		event = getLineBackgroundData(lineOffset, line);
889 		if (event != null && event.lineBackground != null) lineBackground = event.lineBackground;
890 		writeStyledLine(line, lineOffset, ranges, styles, lineBackground, lineIndent, lineAlignment, lineJustify);
891 	}
892 	/**
893 	 * Appends the specified line delimiter to the RTF data.
894 	 *
895 	 * @param lineDelimiter line delimiter to write as RTF.
896 	 * @exception SWTException <ul>
897 	 *   <li>ERROR_IO when the writer is closed.</li>
898 	 * </ul>
899 	 */
900 	@Override
writeLineDelimiter(String lineDelimiter)901 	public void writeLineDelimiter(String lineDelimiter) {
902 		if (isClosed()) {
903 			SWT.error(SWT.ERROR_IO);
904 		}
905 		write(lineDelimiter, 0, lineDelimiter.length());
906 		write("\\par ");
907 	}
908 	/**
909 	 * Appends the specified line text to the RTF data.
910 	 * <p>
911 	 * Use the colors and font styles specified in "styles" and "lineBackground".
912 	 * Formatting is written to reflect the text rendering by the text widget.
913 	 * Style background colors take precedence over the line background color.
914 	 * Background colors are written using the \chshdng0\chcbpat tag (vs. the \cb tag).
915 	 * </p>
916 	 *
917 	 * @param line line text to write as RTF. Must not contain line breaks
918 	 * 	Line breaks should be written using writeLineDelimiter()
919 	 * @param lineOffset offset of the line. 0 based from the start of the
920 	 * 	widget document. Any text occurring before the start offset or after the
921 	 * 	end offset specified during object creation is ignored.
922 	 * @param styles styles to use for formatting. Must not be null.
923 	 * @param lineBackground line background color to use for formatting.
924 	 * 	May be null.
925 	 */
writeStyledLine(String line, int lineOffset, int ranges[], StyleRange[] styles, Color lineBackground, int indent, int alignment, boolean justify)926 	void writeStyledLine(String line, int lineOffset, int ranges[], StyleRange[] styles, Color lineBackground, int indent, int alignment, boolean justify) {
927 		int lineLength = line.length();
928 		int startOffset = getStart();
929 		int writeOffset = startOffset - lineOffset;
930 		if (writeOffset >= lineLength) return;
931 		int lineIndex = Math.max(0, writeOffset);
932 
933 		write("\\fi");
934 		write(indent);
935 		switch (alignment) {
936 			case SWT.LEFT: write("\\ql"); break;
937 			case SWT.CENTER: write("\\qc"); break;
938 			case SWT.RIGHT: write("\\qr"); break;
939 		}
940 		if (justify) write("\\qj");
941 		write(" ");
942 
943 		if (lineBackground != null) {
944 			write("{\\chshdng0\\chcbpat");
945 			write(getColorIndex(lineBackground, DEFAULT_BACKGROUND));
946 			write(" ");
947 		}
948 		int endOffset = startOffset + super.getCharCount();
949 		int lineEndOffset = Math.min(lineLength, endOffset - lineOffset);
950 		for (int i = 0; i < styles.length; i++) {
951 			StyleRange style = styles[i];
952 			int start, end;
953 			if (ranges != null) {
954 				start = ranges[i << 1] - lineOffset;
955 				end = start + ranges[(i << 1) + 1];
956 			} else {
957 				start = style.start - lineOffset;
958 				end = start + style.length;
959 			}
960 			// skip over partial first line
961 			if (end < writeOffset) {
962 				continue;
963 			}
964 			// style starts beyond line end or RTF write end
965 			if (start >= lineEndOffset) {
966 				break;
967 			}
968 			// write any unstyled text
969 			if (lineIndex < start) {
970 				// copy to start of style
971 				// style starting beyond end of write range or end of line
972 				// is guarded against above.
973 				write(line, lineIndex, start);
974 				lineIndex = start;
975 			}
976 			// write styled text
977 			write("{\\cf");
978 			write(getColorIndex(style.foreground, DEFAULT_FOREGROUND));
979 			int colorIndex = getColorIndex(style.background, DEFAULT_BACKGROUND);
980 			if (colorIndex != DEFAULT_BACKGROUND) {
981 				write("\\chshdng0\\chcbpat");
982 				write(colorIndex);
983 			}
984 			int fontStyle = style.fontStyle;
985 			Font font = style.font;
986 			if (font != null) {
987 				int fontIndex = getFontIndex(font);
988 				write("\\f");
989 				write(fontIndex);
990 				FontData fontData = font.getFontData()[0];
991 				write("\\fs");
992 				write(fontData.getHeight() * 2);
993 				fontStyle = fontData.getStyle();
994 			}
995 			if ((fontStyle & SWT.BOLD) != 0) {
996 				write("\\b");
997 			}
998 			if ((fontStyle & SWT.ITALIC) != 0) {
999 				write("\\i");
1000 			}
1001 			if (style.underline) {
1002 				write("\\ul");
1003 			}
1004 			if (style.strikeout) {
1005 				write("\\strike");
1006 			}
1007 			write(" ");
1008 			// copy to end of style or end of write range or end of line
1009 			int copyEnd = Math.min(end, lineEndOffset);
1010 			// guard against invalid styles and let style processing continue
1011 			copyEnd = Math.max(copyEnd, lineIndex);
1012 			write(line, lineIndex, copyEnd);
1013 			if ((fontStyle & SWT.BOLD) != 0) {
1014 				write("\\b0");
1015 			}
1016 			if ((style.fontStyle & SWT.ITALIC) != 0) {
1017 				write("\\i0");
1018 			}
1019 			if (style.underline) {
1020 				write("\\ul0");
1021 			}
1022 			if (style.strikeout) {
1023 				write("\\strike0");
1024 			}
1025 			write("}");
1026 			lineIndex = copyEnd;
1027 		}
1028 		// write unstyled text at the end of the line
1029 		if (lineIndex < lineEndOffset) {
1030 			write(line, lineIndex, lineEndOffset);
1031 		}
1032 		if (lineBackground != null) write("}");
1033 	}
1034 	}
1035 	/**
1036 	 * The <code>TextWriter</code> class is used to write widget content to
1037 	 * a string.  Whole and partial lines and line breaks can be written. To write
1038 	 * partial lines, specify the start and length of the desired segment
1039 	 * during object creation.
1040 	 * <p>
1041 	 * <b>NOTE:</b> <code>toString()</code> is guaranteed to return a valid string only after close()
1042 	 * has been called.
1043 	 * </p>
1044 	 */
1045 	class TextWriter {
1046 		private StringBuilder buffer;
1047 		private int startOffset;	// offset of first character that will be written
1048 		private int endOffset;		// offset of last character that will be written.
1049 									// 0 based from the beginning of the widget text.
1050 		private boolean isClosed = false;
1051 
1052 	/**
1053 	 * Creates a writer that writes content starting at offset "start"
1054 	 * in the document.  <code>start</code> and <code>length</code> can be set to specify partial lines.
1055 	 *
1056 	 * @param start start offset of content to write, 0 based from beginning of document
1057 	 * @param length length of content to write
1058 	 */
TextWriter(int start, int length)1059 	public TextWriter(int start, int length) {
1060 		buffer = new StringBuilder(length);
1061 		startOffset = start;
1062 		endOffset = start + length;
1063 	}
1064 	/**
1065 	 * Closes the writer. Once closed no more content can be written.
1066 	 * <b>NOTE:</b>  <code>toString()</code> is not guaranteed to return a valid string unless
1067 	 * the writer is closed.
1068 	 */
close()1069 	public void close() {
1070 		if (!isClosed) {
1071 			isClosed = true;
1072 		}
1073 	}
1074 	/**
1075 	 * Returns the number of characters to write.
1076 	 * @return the integer number of characters to write
1077 	 */
getCharCount()1078 	public int getCharCount() {
1079 		return endOffset - startOffset;
1080 	}
1081 	/**
1082 	 * Returns the offset where writing starts. 0 based from the start of
1083 	 * the widget text. Used to write partial lines.
1084 	 * @return the integer offset where writing starts
1085 	 */
getStart()1086 	public int getStart() {
1087 		return startOffset;
1088 	}
1089 	/**
1090 	 * Returns whether the writer is closed.
1091 	 * @return a boolean specifying whether or not the writer is closed
1092 	 */
isClosed()1093 	public boolean isClosed() {
1094 		return isClosed;
1095 	}
1096 	/**
1097 	 * Returns the string.  <code>close()</code> must be called before <code>toString()</code>
1098 	 * is guaranteed to return a valid string.
1099 	 *
1100 	 * @return the string
1101 	 */
1102 	@Override
toString()1103 	public String toString() {
1104 		return buffer.toString();
1105 	}
1106 	/**
1107 	 * Appends the given string to the data.
1108 	 */
write(String string)1109 	void write(String string) {
1110 		buffer.append(string);
1111 	}
1112 	/**
1113 	 * Inserts the given string to the data at the specified offset.
1114 	 * <p>
1115 	 * Do nothing if "offset" is &lt; 0 or &gt; getCharCount()
1116 	 * </p>
1117 	 *
1118 	 * @param string text to insert
1119 	 * @param offset offset in the existing data to insert "string" at.
1120 	 */
write(String string, int offset)1121 	void write(String string, int offset) {
1122 		if (offset < 0 || offset > buffer.length()) {
1123 			return;
1124 		}
1125 		buffer.insert(offset, string);
1126 	}
1127 	/**
1128 	 * Appends the given int to the data.
1129 	 */
write(int i)1130 	void write(int i) {
1131 		buffer.append(i);
1132 	}
1133 	/**
1134 	 * Appends the given character to the data.
1135 	 */
write(char i)1136 	void write(char i) {
1137 		buffer.append(i);
1138 	}
1139 	/**
1140 	 * Appends the specified line text to the data.
1141 	 *
1142 	 * @param line line text to write. Must not contain line breaks
1143 	 * 	Line breaks should be written using writeLineDelimiter()
1144 	 * @param lineOffset offset of the line. 0 based from the start of the
1145 	 * 	widget document. Any text occurring before the start offset or after the
1146 	 *	end offset specified during object creation is ignored.
1147 	 * @exception SWTException <ul>
1148 	 *   <li>ERROR_IO when the writer is closed.</li>
1149 	 * </ul>
1150 	 */
writeLine(String line, int lineOffset)1151 	public void writeLine(String line, int lineOffset) {
1152 		if (isClosed) {
1153 			SWT.error(SWT.ERROR_IO);
1154 		}
1155 		int writeOffset = startOffset - lineOffset;
1156 		int lineLength = line.length();
1157 		int lineIndex;
1158 		if (writeOffset >= lineLength) {
1159 			return;							// whole line is outside write range
1160 		} else if (writeOffset > 0) {
1161 			lineIndex = writeOffset;		// line starts before write start
1162 		} else {
1163 			lineIndex = 0;
1164 		}
1165 		int copyEnd = Math.min(lineLength, endOffset - lineOffset);
1166 		if (lineIndex < copyEnd) {
1167 			write(line.substring(lineIndex, copyEnd));
1168 		}
1169 	}
1170 	/**
1171 	 * Appends the specified line delimiter to the data.
1172 	 *
1173 	 * @param lineDelimiter line delimiter to write
1174 	 * @exception SWTException <ul>
1175 	 *   <li>ERROR_IO when the writer is closed.</li>
1176 	 * </ul>
1177 	 */
writeLineDelimiter(String lineDelimiter)1178 	public void writeLineDelimiter(String lineDelimiter) {
1179 		if (isClosed) {
1180 			SWT.error(SWT.ERROR_IO);
1181 		}
1182 		write(lineDelimiter);
1183 	}
1184 	}
1185 
1186 /**
1187  * Constructs a new instance of this class given its parent
1188  * and a style value describing its behavior and appearance.
1189  * <p>
1190  * The style value is either one of the style constants defined in
1191  * class <code>SWT</code> which is applicable to instances of this
1192  * class, or must be built by <em>bitwise OR</em>'ing together
1193  * (that is, using the <code>int</code> "|" operator) two or more
1194  * of those <code>SWT</code> style constants. The class description
1195  * lists the style constants that are applicable to the class.
1196  * Style bits are also inherited from superclasses.
1197  * </p>
1198  *
1199  * @param parent a widget which will be the parent of the new instance (cannot be null)
1200  * @param style the style of widget to construct
1201  *
1202  * @exception IllegalArgumentException <ul>
1203  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
1204  * </ul>
1205  * @exception SWTException <ul>
1206  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
1207  * </ul>
1208  *
1209  * @see SWT#FULL_SELECTION
1210  * @see SWT#MULTI
1211  * @see SWT#READ_ONLY
1212  * @see SWT#SINGLE
1213  * @see SWT#WRAP
1214  * @see #getStyle
1215  */
StyledText(Composite parent, int style)1216 public StyledText(Composite parent, int style) {
1217 	super(parent, checkStyle(style));
1218 	// set the fg in the OS to ensure that these are the same as StyledText, necessary
1219 	// for ensuring that the bg/fg the IME box uses is the same as what StyledText uses
1220 	super.setForeground(getForeground());
1221 	super.setDragDetect(false);
1222 	Display display = getDisplay();
1223 	if ((style & SWT.READ_ONLY) != 0) {
1224 		setEditable(false);
1225 	}
1226 	leftMargin = rightMargin = isBidiCaret() ? BIDI_CARET_WIDTH - 1: 0;
1227 	if ((style & SWT.SINGLE) != 0 && (style & SWT.BORDER) != 0) {
1228 		leftMargin = topMargin = rightMargin = bottomMargin = 2;
1229 	}
1230 	alignment = style & (SWT.LEFT | SWT.RIGHT | SWT.CENTER);
1231 	if (alignment == 0) alignment = SWT.LEFT;
1232 	clipboard = new Clipboard(display);
1233 	installDefaultContent();
1234 	renderer = new StyledTextRenderer(getDisplay(), this);
1235 	renderer.setContent(content);
1236 	renderer.setFont(getFont(), tabLength);
1237 	ime = new IME(this, SWT.NONE);
1238 	defaultCaret = new Caret(this, SWT.NONE);
1239 	if ((style & SWT.WRAP) != 0) {
1240 		setWordWrap(true);
1241 	}
1242 	if (isBidiCaret()) {
1243 		createCaretBitmaps();
1244 		Runnable runnable = () -> {
1245 			int direction = BidiUtil.getKeyboardLanguage() == BidiUtil.KEYBOARD_BIDI ? SWT.RIGHT : SWT.LEFT;
1246 			if (direction == caretDirection) return;
1247 			if (getCaret() != defaultCaret) return;
1248 			Point newCaretPos = getPointAtOffset(caretOffset);
1249 			setCaretLocation(newCaretPos, direction);
1250 		};
1251 		BidiUtil.addLanguageListener(this, runnable);
1252 	}
1253 	setCaret(defaultCaret);
1254 	calculateScrollBars();
1255 	createKeyBindings();
1256 	super.setCursor(display.getSystemCursor(SWT.CURSOR_IBEAM));
1257 	installListeners();
1258 	initializeAccessible();
1259 	setData("DEFAULT_DROP_TARGET_EFFECT", new StyledTextDropTargetEffect(this));
1260 	if (IS_MAC) setData(STYLEDTEXT_KEY);
1261 }
1262 /**
1263  * Adds an extended modify listener. An ExtendedModify event is sent by the
1264  * widget when the widget text has changed.
1265  *
1266  * @param extendedModifyListener the listener
1267  * @exception SWTException <ul>
1268  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1269  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1270  * </ul>
1271  * @exception IllegalArgumentException <ul>
1272  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1273  * </ul>
1274  */
addExtendedModifyListener(ExtendedModifyListener extendedModifyListener)1275 public void addExtendedModifyListener(ExtendedModifyListener extendedModifyListener) {
1276 	checkWidget();
1277 	if (extendedModifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1278 	StyledTextListener typedListener = new StyledTextListener(extendedModifyListener);
1279 	addListener(ST.ExtendedModify, typedListener);
1280 }
1281 /**
1282  * Adds a bidirectional segment listener.
1283  * <p>
1284  * A BidiSegmentEvent is sent
1285  * whenever a line of text is measured or rendered. You can
1286  * specify text ranges in the line that should be treated as if they
1287  * had a different direction than the surrounding text.
1288  * This may be used when adjacent segments of right-to-left text should
1289  * not be reordered relative to each other.
1290  * E.g., multiple Java string literals in a right-to-left language
1291  * should generally remain in logical order to each other, that is, the
1292  * way they are stored.
1293  * </p>
1294  *
1295  * @param listener the listener
1296  * @exception SWTException <ul>
1297  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1298  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1299  * </ul>
1300  * @exception IllegalArgumentException <ul>
1301  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1302  * </ul>
1303  * @see BidiSegmentEvent
1304  * @since 2.0
1305  */
addBidiSegmentListener(BidiSegmentListener listener)1306 public void addBidiSegmentListener(BidiSegmentListener listener) {
1307 	checkWidget();
1308 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1309 	addListener(ST.LineGetSegments, new StyledTextListener(listener));
1310 	resetCache(0, content.getLineCount());
1311 	setCaretLocation();
1312 	super.redraw();
1313 }
1314 /**
1315  * Adds a caret listener. CaretEvent is sent when the caret offset changes.
1316  *
1317  * @param listener the listener
1318  * @exception SWTException <ul>
1319  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1320  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1321  * </ul>
1322  * @exception IllegalArgumentException <ul>
1323  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1324  * </ul>
1325  *
1326  * @since 3.5
1327  */
addCaretListener(CaretListener listener)1328 public void addCaretListener(CaretListener listener) {
1329 	checkWidget();
1330 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1331 	addListener(ST.CaretMoved, new StyledTextListener(listener));
1332 }
1333 /**
1334  * Adds a line background listener. A LineGetBackground event is sent by the
1335  * widget to determine the background color for a line.
1336  *
1337  * @param listener the listener
1338  * @exception SWTException <ul>
1339  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1340  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1341  * </ul>
1342  * @exception IllegalArgumentException <ul>
1343  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1344  * </ul>
1345  */
addLineBackgroundListener(LineBackgroundListener listener)1346 public void addLineBackgroundListener(LineBackgroundListener listener) {
1347 	checkWidget();
1348 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1349 	if (!isListening(ST.LineGetBackground)) {
1350 		renderer.clearLineBackground(0, content.getLineCount());
1351 	}
1352 	addListener(ST.LineGetBackground, new StyledTextListener(listener));
1353 }
1354 /**
1355  * Adds a line style listener. A LineGetStyle event is sent by the widget to
1356  * determine the styles for a line.
1357  *
1358  * @param listener the listener
1359  * @exception SWTException <ul>
1360  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1361  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1362  * </ul>
1363  * @exception IllegalArgumentException <ul>
1364  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1365  * </ul>
1366  */
addLineStyleListener(LineStyleListener listener)1367 public void addLineStyleListener(LineStyleListener listener) {
1368 	checkWidget();
1369 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1370 	if (!isListening(ST.LineGetStyle)) {
1371 		setStyleRanges(0, 0, null, null, true);
1372 		renderer.clearLineStyle(0, content.getLineCount());
1373 	}
1374 	addListener(ST.LineGetStyle, new StyledTextListener(listener));
1375 	setCaretLocation();
1376 }
1377 /**
1378  * Adds a modify listener. A Modify event is sent by the widget when the widget text
1379  * has changed.
1380  *
1381  * @param modifyListener the listener
1382  * @exception SWTException <ul>
1383  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1384  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1385  * </ul>
1386  * @exception IllegalArgumentException <ul>
1387  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1388  * </ul>
1389  */
addModifyListener(ModifyListener modifyListener)1390 public void addModifyListener(ModifyListener modifyListener) {
1391 	checkWidget();
1392 	if (modifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1393 	addListener(SWT.Modify, new TypedListener(modifyListener));
1394 }
1395 /**
1396  * Adds a paint object listener. A paint object event is sent by the widget when an object
1397  * needs to be drawn.
1398  *
1399  * @param listener the listener
1400  * @exception SWTException <ul>
1401  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1402  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1403  * </ul>
1404  * @exception IllegalArgumentException <ul>
1405  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1406  * </ul>
1407  *
1408  * @since 3.2
1409  *
1410  * @see PaintObjectListener
1411  * @see PaintObjectEvent
1412  */
addPaintObjectListener(PaintObjectListener listener)1413 public void addPaintObjectListener(PaintObjectListener listener) {
1414 	checkWidget();
1415 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1416 	addListener(ST.PaintObject, new StyledTextListener(listener));
1417 }
1418 /**
1419  * Adds a selection listener. A Selection event is sent by the widget when the
1420  * user changes the selection.
1421  * <p>
1422  * When <code>widgetSelected</code> is called, the event x and y fields contain
1423  * the start and end caret indices of the selection. The selection values returned are visual
1424  * (i.e., x will always always be &lt;= y).
1425  * No event is sent when the caret is moved while the selection length is 0.
1426  * </p><p>
1427  * <code>widgetDefaultSelected</code> is not called for StyledTexts.
1428  * </p>
1429  *
1430  * @param listener the listener which should be notified when the user changes the receiver's selection
1431 
1432  * @exception IllegalArgumentException <ul>
1433  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1434  * </ul>
1435  * @exception SWTException <ul>
1436  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1437  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1438  * </ul>
1439  *
1440  * @see SelectionListener
1441  * @see #removeSelectionListener
1442  * @see SelectionEvent
1443  */
addSelectionListener(SelectionListener listener)1444 public void addSelectionListener(SelectionListener listener) {
1445 	checkWidget();
1446 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1447 	addListener(SWT.Selection, new TypedListener(listener));
1448 }
1449 /**
1450  * Adds a verify key listener. A VerifyKey event is sent by the widget when a key
1451  * is pressed. The widget ignores the key press if the listener sets the doit field
1452  * of the event to false.
1453  *
1454  * @param listener the listener
1455  * @exception SWTException <ul>
1456  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1457  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1458  * </ul>
1459  * @exception IllegalArgumentException <ul>
1460  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1461  * </ul>
1462  */
addVerifyKeyListener(VerifyKeyListener listener)1463 public void addVerifyKeyListener(VerifyKeyListener listener) {
1464 	checkWidget();
1465 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1466 	addListener(ST.VerifyKey, new StyledTextListener(listener));
1467 }
1468 /**
1469  * Adds a verify listener. A Verify event is sent by the widget when the widget text
1470  * is about to change. The listener can set the event text and the doit field to
1471  * change the text that is set in the widget or to force the widget to ignore the
1472  * text change.
1473  *
1474  * @param verifyListener the listener
1475  * @exception SWTException <ul>
1476  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1477  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1478  * </ul>
1479  * @exception IllegalArgumentException <ul>
1480  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1481  * </ul>
1482  */
addVerifyListener(VerifyListener verifyListener)1483 public void addVerifyListener(VerifyListener verifyListener) {
1484 	checkWidget();
1485 	if (verifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1486 	addListener(SWT.Verify, new TypedListener(verifyListener));
1487 }
1488 /**
1489  * Adds a word movement listener. A movement event is sent when the boundary
1490  * of a word is needed. For example, this occurs during word next and word
1491  * previous actions.
1492  *
1493  * @param movementListener the listener
1494  * @exception SWTException <ul>
1495  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1496  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1497  * </ul>
1498  * @exception IllegalArgumentException <ul>
1499  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1500  * </ul>
1501  *
1502  * @see MovementEvent
1503  * @see MovementListener
1504  * @see #removeWordMovementListener
1505  *
1506  * @since 3.3
1507  */
addWordMovementListener(MovementListener movementListener)1508 public void addWordMovementListener(MovementListener movementListener) {
1509 	checkWidget();
1510 	if (movementListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1511 	addListener(ST.WordNext, new StyledTextListener(movementListener));
1512 	addListener(ST.WordPrevious, new StyledTextListener(movementListener));
1513 }
1514 /**
1515  * Appends a string to the text at the end of the widget.
1516  *
1517  * @param string the string to be appended
1518  * @see #replaceTextRange(int,int,String)
1519  * @exception SWTException <ul>
1520  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1521  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1522  * </ul>
1523  * @exception IllegalArgumentException <ul>
1524  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
1525  * </ul>
1526  */
append(String string)1527 public void append(String string) {
1528 	checkWidget();
1529 	if (string == null) {
1530 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
1531 	}
1532 	int lastChar = Math.max(getCharCount(), 0);
1533 	replaceTextRange(lastChar, 0, string);
1534 }
1535 /**
1536  * Calculates the scroll bars
1537  */
calculateScrollBars()1538 void calculateScrollBars() {
1539 	ScrollBar horizontalBar = getHorizontalBar();
1540 	ScrollBar verticalBar = getVerticalBar();
1541 	setScrollBars(true);
1542 	if (verticalBar != null) {
1543 		verticalBar.setIncrement(getVerticalIncrement());
1544 	}
1545 	if (horizontalBar != null) {
1546 		horizontalBar.setIncrement(getHorizontalIncrement());
1547 	}
1548 }
1549 /**
1550  * Calculates the top index based on the current vertical scroll offset.
1551  * The top index is the index of the topmost fully visible line or the
1552  * topmost partially visible line if no line is fully visible.
1553  * The top index starts at 0.
1554  */
calculateTopIndex(int delta)1555 void calculateTopIndex(int delta) {
1556 	int oldDelta = delta;
1557 	int oldTopIndex = topIndex;
1558 	int oldTopIndexY = topIndexY;
1559 	if (isFixedLineHeight()) {
1560 		int verticalIncrement = getVerticalIncrement();
1561 		if (verticalIncrement == 0) {
1562 			return;
1563 		}
1564 		topIndex = Compatibility.ceil(getVerticalScrollOffset(), verticalIncrement);
1565 		// Set top index to partially visible top line if no line is fully
1566 		// visible but at least some of the widget client area is visible.
1567 		// Fixes bug 15088.
1568 		if (topIndex > 0) {
1569 			if (clientAreaHeight > 0) {
1570 				int bottomPixel = getVerticalScrollOffset() + clientAreaHeight;
1571 				int fullLineTopPixel = topIndex * verticalIncrement;
1572 				int fullLineVisibleHeight = bottomPixel - fullLineTopPixel;
1573 				// set top index to partially visible line if no line fully fits in
1574 				// client area or if space is available but not used (the latter should
1575 				// never happen because we use claimBottomFreeSpace)
1576 				if (fullLineVisibleHeight < verticalIncrement) {
1577 					topIndex = getVerticalScrollOffset() / verticalIncrement;
1578 				}
1579 			} else if (topIndex >= content.getLineCount()) {
1580 				topIndex = content.getLineCount() - 1;
1581 			}
1582 		}
1583 	} else {
1584 		if (delta >= 0) {
1585 			delta -= topIndexY;
1586 			int lineIndex = topIndex;
1587 			int lineCount = content.getLineCount();
1588 			while (lineIndex < lineCount) {
1589 				if (delta <= 0) break;
1590 				delta -= renderer.getCachedLineHeight(lineIndex++);
1591 			}
1592 			if (lineIndex < lineCount && -delta + renderer.getCachedLineHeight(lineIndex) <= clientAreaHeight - topMargin - bottomMargin) {
1593 				topIndex = lineIndex;
1594 				topIndexY = -delta;
1595 			} else {
1596 				topIndex = lineIndex - 1;
1597 				topIndexY = -renderer.getCachedLineHeight(topIndex) - delta;
1598 			}
1599 		} else {
1600 			delta -= topIndexY;
1601 			int lineIndex = topIndex;
1602 			while (lineIndex > 0) {
1603 				int lineHeight = renderer.getCachedLineHeight(lineIndex - 1);
1604 				if (delta + lineHeight > 0) break;
1605 				delta += lineHeight;
1606 				lineIndex--;
1607 			}
1608 			if (lineIndex == 0 || -delta + renderer.getCachedLineHeight(lineIndex) <= clientAreaHeight - topMargin - bottomMargin) {
1609 				topIndex = lineIndex;
1610 				topIndexY = - delta;
1611 			} else {
1612 				topIndex = lineIndex - 1;
1613 				topIndexY = - renderer.getCachedLineHeight(topIndex) - delta;
1614 			}
1615 		}
1616 	}
1617 	if (topIndex < 0) {
1618 		// TODO: This logging is in place to determine why topIndex is getting set to negative values.
1619 		// It should be deleted once we fix the root cause of this issue. See bug 487254 for details.
1620 		System.err.println("StyledText: topIndex was " + topIndex
1621 				+ ", isFixedLineHeight() = " + isFixedLineHeight()
1622 				+ ", delta = " + delta
1623 				+ ", content.getLineCount() = " + content.getLineCount()
1624 				+ ", clientAreaHeight = " + clientAreaHeight
1625 				+ ", oldTopIndex = " + oldTopIndex
1626 				+ ", oldTopIndexY = " + oldTopIndexY
1627 				+ ", getVerticalScrollOffset = " + getVerticalScrollOffset()
1628 				+ ", oldDelta = " + oldDelta
1629 				+ ", getVerticalIncrement() = " + getVerticalIncrement());
1630 		topIndex = 0;
1631 	}
1632 	if (topIndex != oldTopIndex || oldTopIndexY != topIndexY) {
1633 		int width = renderer.getWidth();
1634 		renderer.calculateClientArea();
1635 		if (width != renderer.getWidth()) {
1636 			setScrollBars(false);
1637 		}
1638 	}
1639 }
1640 /**
1641  * Hides the scroll bars if widget is created in single line mode.
1642  */
checkStyle(int style)1643 static int checkStyle(int style) {
1644 	if ((style & SWT.SINGLE) != 0) {
1645 		style &= ~(SWT.H_SCROLL | SWT.V_SCROLL | SWT.WRAP | SWT.MULTI);
1646 	} else {
1647 		style |= SWT.MULTI;
1648 		if ((style & SWT.WRAP) != 0) {
1649 			style &= ~SWT.H_SCROLL;
1650 		}
1651 	}
1652 	style |= SWT.NO_REDRAW_RESIZE | SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND;
1653 	/* Clear SWT.CENTER to avoid the conflict with SWT.EMBEDDED */
1654 	return style & ~SWT.CENTER;
1655 }
1656 /**
1657  * Scrolls down the text to use new space made available by a resize or by
1658  * deleted lines.
1659  */
claimBottomFreeSpace()1660 void claimBottomFreeSpace() {
1661 	if (ime.getCompositionOffset() != -1) return;
1662 	if (isFixedLineHeight()) {
1663 		int newVerticalOffset = Math.max(0, renderer.getHeight() - clientAreaHeight);
1664 		if (newVerticalOffset < getVerticalScrollOffset()) {
1665 			scrollVertical(newVerticalOffset - getVerticalScrollOffset(), true);
1666 		}
1667 	} else {
1668 		int bottomIndex = getPartialBottomIndex();
1669 		int height = getLinePixel(bottomIndex + 1);
1670 		if (clientAreaHeight > height) {
1671 			scrollVertical(-getAvailableHeightAbove(clientAreaHeight - height), true);
1672 		}
1673 	}
1674 }
1675 /**
1676  * Scrolls text to the right to use new space made available by a resize.
1677  */
claimRightFreeSpace()1678 void claimRightFreeSpace() {
1679 	int newHorizontalOffset = Math.max(0, renderer.getWidth() - clientAreaWidth);
1680 	if (newHorizontalOffset < horizontalScrollOffset) {
1681 		// item is no longer drawn past the right border of the client area
1682 		// align the right end of the item with the right border of the
1683 		// client area (window is scrolled right).
1684 		scrollHorizontal(newHorizontalOffset - horizontalScrollOffset, true);
1685 	}
1686 }
clearBlockSelection(boolean reset, boolean sendEvent)1687 void clearBlockSelection(boolean reset, boolean sendEvent) {
1688 	if (reset) resetSelection();
1689 	blockXAnchor = blockYAnchor = -1;
1690 	blockXLocation = blockYLocation = -1;
1691 	caretDirection = SWT.NULL;
1692 	updateCaretVisibility();
1693 	super.redraw();
1694 	if (sendEvent) sendSelectionEvent();
1695 }
1696 /**
1697  * Removes the widget selection.
1698  *
1699  * @param sendEvent a Selection event is sent when set to true and when the selection is actually reset.
1700  */
clearSelection(boolean sendEvent)1701 void clearSelection(boolean sendEvent) {
1702 	int selectionStart = selection.x;
1703 	int selectionEnd = selection.y;
1704 	resetSelection();
1705 	// redraw old selection, if any
1706 	if (selectionEnd - selectionStart > 0) {
1707 		int length = content.getCharCount();
1708 		// called internally to remove selection after text is removed
1709 		// therefore make sure redraw range is valid.
1710 		int redrawStart = Math.min(selectionStart, length);
1711 		int redrawEnd = Math.min(selectionEnd, length);
1712 		if (redrawEnd - redrawStart > 0) {
1713 			internalRedrawRange(redrawStart, redrawEnd - redrawStart);
1714 		}
1715 		if (sendEvent) {
1716 			sendSelectionEvent();
1717 		}
1718 	}
1719 }
1720 @Override
computeSize(int wHint, int hHint, boolean changed)1721 public Point computeSize (int wHint, int hHint, boolean changed) {
1722 	checkWidget();
1723 	int lineCount = (getStyle() & SWT.SINGLE) != 0 ? 1 : content.getLineCount();
1724 	int width = 0;
1725 	int height = 0;
1726 	if (wHint == SWT.DEFAULT || hHint == SWT.DEFAULT) {
1727 		Display display = getDisplay();
1728 		int maxHeight = display.getClientArea().height;
1729 		for (int lineIndex = 0; lineIndex < lineCount; lineIndex++) {
1730 			TextLayout layout = renderer.getTextLayout(lineIndex);
1731 			int wrapWidth = layout.getWidth();
1732 			if (wordWrap) layout.setWidth(wHint == 0 ? 1 : wHint == SWT.DEFAULT ? SWT.DEFAULT : Math.max(1, wHint - leftMargin - rightMargin));
1733 			Rectangle rect = layout.getBounds();
1734 			height += rect.height;
1735 			width = Math.max(width, rect.width);
1736 			layout.setWidth(wrapWidth);
1737 			renderer.disposeTextLayout(layout);
1738 			if (isFixedLineHeight() && height > maxHeight) break;
1739 		}
1740 		if (isFixedLineHeight()) {
1741 			height = lineCount * renderer.getLineHeight();
1742 		}
1743 	}
1744 	// Use default values if no text is defined.
1745 	if (width == 0) width = DEFAULT_WIDTH;
1746 	if (height == 0) height = DEFAULT_HEIGHT;
1747 	if (wHint != SWT.DEFAULT) width = wHint;
1748 	if (hHint != SWT.DEFAULT) height = hHint;
1749 	int wTrim = getLeftMargin() + rightMargin + getCaretWidth();
1750 	int hTrim = topMargin + bottomMargin;
1751 	Rectangle rect = computeTrim(0, 0, width + wTrim, height + hTrim);
1752 	return new Point (rect.width, rect.height);
1753 }
1754 /**
1755  * Copies the selected text to the <code>DND.CLIPBOARD</code> clipboard.
1756  * <p>
1757  * The text will be put on the clipboard in plain text format and RTF format.
1758  * The <code>DND.CLIPBOARD</code> clipboard is used for data that is
1759  * transferred by keyboard accelerator (such as Ctrl+C/Ctrl+V) or
1760  * by menu action.
1761  * </p>
1762  *
1763  * @exception SWTException <ul>
1764  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1765  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1766  * </ul>
1767  */
copy()1768 public void copy() {
1769 	checkWidget();
1770 	copySelection(DND.CLIPBOARD);
1771 }
1772 /**
1773  * Copies the selected text to the specified clipboard.  The text will be put in the
1774  * clipboard in plain text format and RTF format.
1775  * <p>
1776  * The clipboardType is  one of the clipboard constants defined in class
1777  * <code>DND</code>.  The <code>DND.CLIPBOARD</code>  clipboard is
1778  * used for data that is transferred by keyboard accelerator (such as Ctrl+C/Ctrl+V)
1779  * or by menu action.  The <code>DND.SELECTION_CLIPBOARD</code>
1780  * clipboard is used for data that is transferred by selecting text and pasting
1781  * with the middle mouse button.
1782  * </p>
1783  *
1784  * @param clipboardType indicates the type of clipboard
1785  *
1786  * @exception SWTException <ul>
1787  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1788  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1789  * </ul>
1790  *
1791  * @since 3.1
1792  */
copy(int clipboardType)1793 public void copy(int clipboardType) {
1794 	checkWidget();
1795 	copySelection(clipboardType);
1796 }
copySelection(int type)1797 boolean copySelection(int type) {
1798 	if (type != DND.CLIPBOARD && type != DND.SELECTION_CLIPBOARD) return false;
1799 	try {
1800 		if (blockSelection && blockXLocation != -1) {
1801 			String text = getBlockSelectionText(PlatformLineDelimiter);
1802 			if (text.length() > 0) {
1803 				//TODO RTF support
1804 				TextTransfer plainTextTransfer = TextTransfer.getInstance();
1805 				Object[] data = new Object[]{text};
1806 				Transfer[] types = new Transfer[]{plainTextTransfer};
1807 				clipboard.setContents(data, types, type);
1808 				return true;
1809 			}
1810 		} else {
1811 			int length = selection.y - selection.x;
1812 			if (length > 0) {
1813 				setClipboardContent(selection.x, length, type);
1814 				return true;
1815 			}
1816 		}
1817 	} catch (SWTError error) {
1818 		// Copy to clipboard failed. This happens when another application
1819 		// is accessing the clipboard while we copy. Ignore the error.
1820 		// Rethrow all other errors. Fixes bug 17578.
1821 		if (error.code != DND.ERROR_CANNOT_SET_CLIPBOARD) {
1822 			throw error;
1823 		}
1824 	}
1825 	return false;
1826 }
1827 /**
1828  * Returns the alignment of the widget.
1829  *
1830  * @return the alignment
1831  *
1832  * @exception SWTException <ul>
1833  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1834  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1835  * </ul>
1836  *
1837  * @see #getLineAlignment(int)
1838  *
1839  * @since 3.2
1840  */
getAlignment()1841 public int getAlignment() {
1842 	checkWidget();
1843 	return alignment;
1844 }
1845 /**
1846  * Returns the Always Show Scrollbars flag.  True if the scrollbars are
1847  * always shown even if they are not required.  False if the scrollbars are only
1848  * visible when some part of the content needs to be scrolled to be seen.
1849  * The H_SCROLL and V_SCROLL style bits are also required to enable scrollbars in the
1850  * horizontal and vertical directions.
1851  *
1852  * @return the Always Show Scrollbars flag value
1853  *
1854  * @exception SWTException <ul>
1855  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1856  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1857  * </ul>
1858  *
1859  * @since 3.8
1860  */
getAlwaysShowScrollBars()1861 public boolean getAlwaysShowScrollBars() {
1862 	checkWidget();
1863 	return alwaysShowScroll;
1864 }
getAvailableHeightAbove(int height)1865 int getAvailableHeightAbove(int height) {
1866 	int maxHeight = verticalScrollOffset;
1867 	if (maxHeight == -1) {
1868 		int lineIndex = topIndex - 1;
1869 		maxHeight = -topIndexY;
1870 		if (topIndexY > 0) {
1871 			maxHeight += renderer.getLineHeight(lineIndex--);
1872 		}
1873 		while (height > maxHeight && lineIndex >= 0) {
1874 			maxHeight += renderer.getLineHeight(lineIndex--);
1875 		}
1876 	}
1877 	return Math.min(height, maxHeight);
1878 }
getAvailableHeightBellow(int height)1879 int getAvailableHeightBellow(int height) {
1880 	int partialBottomIndex = getPartialBottomIndex();
1881 	int topY = getLinePixel(partialBottomIndex);
1882 	int lineHeight = renderer.getLineHeight(partialBottomIndex);
1883 	int availableHeight = 0;
1884 	int clientAreaHeight = this.clientAreaHeight - topMargin - bottomMargin;
1885 	if (topY + lineHeight > clientAreaHeight) {
1886 		availableHeight = lineHeight - (clientAreaHeight - topY);
1887 	}
1888 	int lineIndex = partialBottomIndex + 1;
1889 	int lineCount = content.getLineCount();
1890 	while (height > availableHeight && lineIndex < lineCount) {
1891 		availableHeight += renderer.getLineHeight(lineIndex++);
1892 	}
1893 	return Math.min(height, availableHeight);
1894 }
1895 /**
1896  * Returns the color of the margins.
1897  *
1898  * @return the color of the margins.
1899  * @exception SWTException <ul>
1900  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1901  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1902  * </ul>
1903  *
1904  * @since 3.5
1905  */
getMarginColor()1906 public Color getMarginColor() {
1907 	checkWidget();
1908 	return marginColor != null ? marginColor : getBackground();
1909 }
1910 /**
1911  * Returns a string that uses only the line delimiter specified by the
1912  * StyledTextContent implementation.
1913  * <p>
1914  * Returns only the first line if the widget has the SWT.SINGLE style.
1915  * </p>
1916  *
1917  * @param text the text that may have line delimiters that don't
1918  * 	match the model line delimiter. Possible line delimiters
1919  * 	are CR ('\r'), LF ('\n'), CR/LF ("\r\n")
1920  * @return the converted text that only uses the line delimiter
1921  * 	specified by the model. Returns only the first line if the widget
1922  * 	has the SWT.SINGLE style.
1923  */
getModelDelimitedText(String text)1924 String getModelDelimitedText(String text) {
1925 	int length = text.length();
1926 	if (length == 0) {
1927 		return text;
1928 	}
1929 	int crIndex = 0;
1930 	int lfIndex = 0;
1931 	int i = 0;
1932 	StringBuilder convertedText = new StringBuilder(length);
1933 	String delimiter = getLineDelimiter();
1934 	while (i < length) {
1935 		if (crIndex != -1) {
1936 			crIndex = text.indexOf(SWT.CR, i);
1937 		}
1938 		if (lfIndex != -1) {
1939 			lfIndex = text.indexOf(SWT.LF, i);
1940 		}
1941 		if (lfIndex == -1 && crIndex == -1) {	// no more line breaks?
1942 			break;
1943 		} else if ((crIndex < lfIndex && crIndex != -1) || lfIndex == -1) {
1944 			convertedText.append(text.substring(i, crIndex));
1945 			if (lfIndex == crIndex + 1) {		// CR/LF combination?
1946 				i = lfIndex + 1;
1947 			} else {
1948 				i = crIndex + 1;
1949 			}
1950 		} else {									// LF occurs before CR!
1951 			convertedText.append(text.substring(i, lfIndex));
1952 			i = lfIndex + 1;
1953 		}
1954 		if (isSingleLine()) {
1955 			break;
1956 		}
1957 		convertedText.append(delimiter);
1958 	}
1959 	// copy remaining text if any and if not in single line mode or no
1960 	// text copied thus far (because there only is one line)
1961 	if (i < length && (!isSingleLine() || convertedText.length() == 0)) {
1962 		convertedText.append(text.substring(i));
1963 	}
1964 	return convertedText.toString();
1965 }
checkDragDetect(Event event)1966 boolean checkDragDetect(Event event) {
1967 	if (!isListening(SWT.DragDetect)) return false;
1968 	if (event.button != 1) return false;
1969 	if (blockSelection && blockXLocation != -1) {
1970 		Rectangle rect = getBlockSelectionRectangle();
1971 		if (rect.contains(event.x, event.y)) {
1972 			return dragDetect(event);
1973 		}
1974 	} else {
1975 		if (selection.x == selection.y) return false;
1976 		int offset = getOffsetAtPoint(event.x, event.y, null, true);
1977 		if (selection.x <= offset && offset < selection.y) {
1978 			return dragDetect(event);
1979 		}
1980 
1981 	}
1982 	return false;
1983 }
1984 
1985 /**
1986  * Creates default key bindings.
1987  */
createKeyBindings()1988 void createKeyBindings() {
1989 	int nextKey = isMirrored() ? SWT.ARROW_LEFT : SWT.ARROW_RIGHT;
1990 	int previousKey = isMirrored() ? SWT.ARROW_RIGHT : SWT.ARROW_LEFT;
1991 
1992 	// Navigation
1993 	setKeyBinding(SWT.ARROW_UP, ST.LINE_UP);
1994 	setKeyBinding(SWT.ARROW_DOWN, ST.LINE_DOWN);
1995 	if (IS_MAC) {
1996 		setKeyBinding(previousKey | SWT.MOD1, ST.LINE_START);
1997 		setKeyBinding(nextKey | SWT.MOD1, ST.LINE_END);
1998 		setKeyBinding(SWT.HOME, ST.TEXT_START);
1999 		setKeyBinding(SWT.END, ST.TEXT_END);
2000 		setKeyBinding(SWT.ARROW_UP | SWT.MOD1, ST.TEXT_START);
2001 		setKeyBinding(SWT.ARROW_DOWN | SWT.MOD1, ST.TEXT_END);
2002 		setKeyBinding(nextKey | SWT.MOD3, ST.WORD_NEXT);
2003 		setKeyBinding(previousKey | SWT.MOD3, ST.WORD_PREVIOUS);
2004 	} else {
2005 		setKeyBinding(SWT.HOME, ST.LINE_START);
2006 		setKeyBinding(SWT.END, ST.LINE_END);
2007 		setKeyBinding(SWT.HOME | SWT.MOD1, ST.TEXT_START);
2008 		setKeyBinding(SWT.END | SWT.MOD1, ST.TEXT_END);
2009 		setKeyBinding(nextKey | SWT.MOD1, ST.WORD_NEXT);
2010 		setKeyBinding(previousKey | SWT.MOD1, ST.WORD_PREVIOUS);
2011 	}
2012 	setKeyBinding(SWT.PAGE_UP, ST.PAGE_UP);
2013 	setKeyBinding(SWT.PAGE_DOWN, ST.PAGE_DOWN);
2014 	setKeyBinding(SWT.PAGE_UP | SWT.MOD1, ST.WINDOW_START);
2015 	setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1, ST.WINDOW_END);
2016 	setKeyBinding(nextKey, ST.COLUMN_NEXT);
2017 	setKeyBinding(previousKey, ST.COLUMN_PREVIOUS);
2018 
2019 	// Selection
2020 	setKeyBinding(SWT.ARROW_UP | SWT.MOD2, ST.SELECT_LINE_UP);
2021 	setKeyBinding(SWT.ARROW_DOWN | SWT.MOD2, ST.SELECT_LINE_DOWN);
2022 	if (IS_MAC) {
2023 		setKeyBinding(previousKey | SWT.MOD1 | SWT.MOD2, ST.SELECT_LINE_START);
2024 		setKeyBinding(nextKey | SWT.MOD1 | SWT.MOD2, ST.SELECT_LINE_END);
2025 		setKeyBinding(SWT.HOME | SWT.MOD2, ST.SELECT_TEXT_START);
2026 		setKeyBinding(SWT.END | SWT.MOD2, ST.SELECT_TEXT_END);
2027 		setKeyBinding(SWT.ARROW_UP | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_START);
2028 		setKeyBinding(SWT.ARROW_DOWN | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_END);
2029 		setKeyBinding(nextKey | SWT.MOD2 | SWT.MOD3, ST.SELECT_WORD_NEXT);
2030 		setKeyBinding(previousKey | SWT.MOD2 | SWT.MOD3, ST.SELECT_WORD_PREVIOUS);
2031 	} else  {
2032 		setKeyBinding(SWT.HOME | SWT.MOD2, ST.SELECT_LINE_START);
2033 		setKeyBinding(SWT.END | SWT.MOD2, ST.SELECT_LINE_END);
2034 		setKeyBinding(SWT.HOME | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_START);
2035 		setKeyBinding(SWT.END | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_END);
2036 		setKeyBinding(nextKey | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_NEXT);
2037 		setKeyBinding(previousKey | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_PREVIOUS);
2038 	}
2039 	setKeyBinding(SWT.PAGE_UP | SWT.MOD2, ST.SELECT_PAGE_UP);
2040 	setKeyBinding(SWT.PAGE_DOWN | SWT.MOD2, ST.SELECT_PAGE_DOWN);
2041 	setKeyBinding(SWT.PAGE_UP | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_START);
2042 	setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_END);
2043 	setKeyBinding(nextKey | SWT.MOD2, ST.SELECT_COLUMN_NEXT);
2044 	setKeyBinding(previousKey | SWT.MOD2, ST.SELECT_COLUMN_PREVIOUS);
2045 
2046 	// Modification
2047 	// Cut, Copy, Paste
2048 	setKeyBinding('X' | SWT.MOD1, ST.CUT);
2049 	setKeyBinding('C' | SWT.MOD1, ST.COPY);
2050 	setKeyBinding('V' | SWT.MOD1, ST.PASTE);
2051 	if (IS_MAC) {
2052 		setKeyBinding(SWT.DEL | SWT.MOD2, ST.DELETE_NEXT);
2053 		setKeyBinding(SWT.BS | SWT.MOD3, ST.DELETE_WORD_PREVIOUS);
2054 		setKeyBinding(SWT.DEL | SWT.MOD3, ST.DELETE_WORD_NEXT);
2055 	} else {
2056 		// Cut, Copy, Paste Wordstar style
2057 		setKeyBinding(SWT.DEL | SWT.MOD2, ST.CUT);
2058 		setKeyBinding(SWT.INSERT | SWT.MOD1, ST.COPY);
2059 		setKeyBinding(SWT.INSERT | SWT.MOD2, ST.PASTE);
2060 	}
2061 	setKeyBinding(SWT.BS | SWT.MOD2, ST.DELETE_PREVIOUS);
2062 	setKeyBinding(SWT.BS, ST.DELETE_PREVIOUS);
2063 	setKeyBinding(SWT.DEL, ST.DELETE_NEXT);
2064 	setKeyBinding(SWT.BS | SWT.MOD1, ST.DELETE_WORD_PREVIOUS);
2065 	setKeyBinding(SWT.DEL | SWT.MOD1, ST.DELETE_WORD_NEXT);
2066 
2067 	// Miscellaneous
2068 	setKeyBinding(SWT.INSERT, ST.TOGGLE_OVERWRITE);
2069 }
2070 /**
2071  * Create the bitmaps to use for the caret in bidi mode.  This
2072  * method only needs to be called upon widget creation and when the
2073  * font changes (the caret bitmap height needs to match font height).
2074  */
createCaretBitmaps()2075 void createCaretBitmaps() {
2076 	int caretWidth = BIDI_CARET_WIDTH;
2077 	Display display = getDisplay();
2078 	if (leftCaretBitmap != null) {
2079 		if (defaultCaret != null && leftCaretBitmap.equals(defaultCaret.getImage())) {
2080 			defaultCaret.setImage(null);
2081 		}
2082 		leftCaretBitmap.dispose();
2083 	}
2084 	int lineHeight = renderer.getLineHeight();
2085 	leftCaretBitmap = new Image(display, caretWidth, lineHeight);
2086 	GC gc = new GC (leftCaretBitmap);
2087 	gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
2088 	gc.fillRectangle(0, 0, caretWidth, lineHeight);
2089 	gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
2090 	gc.drawLine(0,0,0,lineHeight);
2091 	gc.drawLine(0,0,caretWidth-1,0);
2092 	gc.drawLine(0,1,1,1);
2093 	gc.dispose();
2094 
2095 	if (rightCaretBitmap != null) {
2096 		if (defaultCaret != null && rightCaretBitmap.equals(defaultCaret.getImage())) {
2097 			defaultCaret.setImage(null);
2098 		}
2099 		rightCaretBitmap.dispose();
2100 	}
2101 	rightCaretBitmap = new Image(display, caretWidth, lineHeight);
2102 	gc = new GC (rightCaretBitmap);
2103 	gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
2104 	gc.fillRectangle(0, 0, caretWidth, lineHeight);
2105 	gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
2106 	gc.drawLine(caretWidth-1,0,caretWidth-1,lineHeight);
2107 	gc.drawLine(0,0,caretWidth-1,0);
2108 	gc.drawLine(caretWidth-1,1,1,1);
2109 	gc.dispose();
2110 }
2111 /**
2112  * Moves the selected text to the clipboard.  The text will be put in the
2113  * clipboard in plain text format and RTF format.
2114  *
2115  * @exception SWTException <ul>
2116  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2117  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2118  * </ul>
2119  */
cut()2120 public void cut() {
2121 	checkWidget();
2122 	// Abort cut operation if copy to clipboard fails.
2123 	// Fixes bug 21030.
2124 	if (copySelection(DND.CLIPBOARD)) {
2125 		if (blockSelection && blockXLocation != -1) {
2126 			insertBlockSelectionText((char)0, SWT.NULL);
2127 		} else {
2128 			doDelete();
2129 		}
2130 	}
2131 }
2132 /**
2133  * A mouse move event has occurred.  See if we should start autoscrolling.  If
2134  * the move position is outside of the client area, initiate autoscrolling.
2135  * Otherwise, we've moved back into the widget so end autoscrolling.
2136  */
doAutoScroll(Event event)2137 void doAutoScroll(Event event) {
2138 	int caretLine = getCaretLine();
2139 	if (event.y > clientAreaHeight - bottomMargin && caretLine != content.getLineCount() - 1) {
2140 		doAutoScroll(SWT.DOWN, event.y - (clientAreaHeight - bottomMargin));
2141 	} else if (event.y < topMargin && caretLine != 0) {
2142 		doAutoScroll(SWT.UP, topMargin - event.y);
2143 	} else if (event.x < leftMargin && !wordWrap) {
2144 		doAutoScroll(ST.COLUMN_PREVIOUS, leftMargin - event.x);
2145 	} else if (event.x > clientAreaWidth - rightMargin && !wordWrap) {
2146 		doAutoScroll(ST.COLUMN_NEXT, event.x - (clientAreaWidth - rightMargin));
2147 	} else {
2148 		endAutoScroll();
2149 	}
2150 }
2151 /**
2152  * Initiates autoscrolling.
2153  *
2154  * @param direction SWT.UP, SWT.DOWN, SWT.COLUMN_NEXT, SWT.COLUMN_PREVIOUS
2155  */
doAutoScroll(int direction, int distance)2156 void doAutoScroll(int direction, int distance) {
2157 	autoScrollDistance = distance;
2158 	// If we're already autoscrolling in the given direction do nothing
2159 	if (autoScrollDirection == direction) {
2160 		return;
2161 	}
2162 
2163 	Runnable timer = null;
2164 	final Display display = getDisplay();
2165 	// Set a timer that will simulate the user pressing and holding
2166 	// down a cursor key (i.e., arrowUp, arrowDown).
2167 	if (direction == SWT.UP) {
2168 		timer = new Runnable() {
2169 			@Override
2170 			public void run() {
2171 				/* Bug 437357 - NPE in StyledText.getCaretLine
2172 				 * StyledText.content is null at times, probably because the
2173 				 * widget itself has been disposed.
2174 				 */
2175 				if (isDisposed()) return;
2176 				if (autoScrollDirection == SWT.UP) {
2177 					if (blockSelection) {
2178 						int verticalScrollOffset = getVerticalScrollOffset();
2179 						int y = blockYLocation - verticalScrollOffset;
2180 						int pixels = Math.max(-autoScrollDistance, -verticalScrollOffset);
2181 						if (pixels != 0) {
2182 							setBlockSelectionLocation(blockXLocation - horizontalScrollOffset, y + pixels, true);
2183 							scrollVertical(pixels, true);
2184 						}
2185 					} else {
2186 						doSelectionPageUp(autoScrollDistance);
2187 					}
2188 					display.timerExec(V_SCROLL_RATE, this);
2189 				}
2190 			}
2191 		};
2192 		autoScrollDirection = direction;
2193 		display.timerExec(V_SCROLL_RATE, timer);
2194 	} else if (direction == SWT.DOWN) {
2195 		timer = new Runnable() {
2196 			@Override
2197 			public void run() {
2198 				/* Bug 437357 - NPE in StyledText.getCaretLine
2199 				 * StyledText.content is null at times, probably because the
2200 				 * widget itself has been disposed.
2201 				 */
2202 				if (isDisposed()) return;
2203 				if (autoScrollDirection == SWT.DOWN) {
2204 					if (blockSelection) {
2205 						int verticalScrollOffset = getVerticalScrollOffset();
2206 						int y = blockYLocation - verticalScrollOffset;
2207 						int max = renderer.getHeight() - verticalScrollOffset - clientAreaHeight;
2208 						int pixels = Math.min(autoScrollDistance, Math.max(0,max));
2209 						if (pixels != 0) {
2210 							setBlockSelectionLocation(blockXLocation - horizontalScrollOffset, y + pixels, true);
2211 							scrollVertical(pixels, true);
2212 						}
2213 					} else {
2214 						doSelectionPageDown(autoScrollDistance);
2215 					}
2216 					display.timerExec(V_SCROLL_RATE, this);
2217 				}
2218 			}
2219 		};
2220 		autoScrollDirection = direction;
2221 		display.timerExec(V_SCROLL_RATE, timer);
2222 	} else if (direction == ST.COLUMN_NEXT) {
2223 		timer = new Runnable() {
2224 			@Override
2225 			public void run() {
2226 				/* Bug 437357 - NPE in StyledText.getCaretLine
2227 				 * StyledText.content is null at times, probably because the
2228 				 * widget itself has been disposed.
2229 				 */
2230 				if (isDisposed()) return;
2231 				if (autoScrollDirection == ST.COLUMN_NEXT) {
2232 					if (blockSelection) {
2233 						int x = blockXLocation - horizontalScrollOffset;
2234 						int max = renderer.getWidth() - horizontalScrollOffset - clientAreaWidth;
2235 						int pixels = Math.min(autoScrollDistance, Math.max(0,max));
2236 						if (pixels != 0) {
2237 							setBlockSelectionLocation(x + pixels, blockYLocation - getVerticalScrollOffset(), true);
2238 							scrollHorizontal(pixels, true);
2239 						}
2240 					} else {
2241 						doVisualNext();
2242 						setMouseWordSelectionAnchor();
2243 						doMouseSelection();
2244 					}
2245 					display.timerExec(H_SCROLL_RATE, this);
2246 				}
2247 			}
2248 		};
2249 		autoScrollDirection = direction;
2250 		display.timerExec(H_SCROLL_RATE, timer);
2251 	} else if (direction == ST.COLUMN_PREVIOUS) {
2252 		timer = new Runnable() {
2253 			@Override
2254 			public void run() {
2255 				/* Bug 437357 - NPE in StyledText.getCaretLine
2256 				 * StyledText.content is null at times, probably because the
2257 				 * widget itself has been disposed.
2258 				 */
2259 				if (isDisposed()) return;
2260 				if (autoScrollDirection == ST.COLUMN_PREVIOUS) {
2261 					if (blockSelection) {
2262 						int x = blockXLocation - horizontalScrollOffset;
2263 						int pixels = Math.max(-autoScrollDistance, -horizontalScrollOffset);
2264 						if (pixels != 0) {
2265 							setBlockSelectionLocation(x + pixels, blockYLocation - getVerticalScrollOffset(), true);
2266 							scrollHorizontal(pixels, true);
2267 						}
2268 					} else {
2269 						doVisualPrevious();
2270 						setMouseWordSelectionAnchor();
2271 						doMouseSelection();
2272 					}
2273 					display.timerExec(H_SCROLL_RATE, this);
2274 				}
2275 			}
2276 		};
2277 		autoScrollDirection = direction;
2278 		display.timerExec(H_SCROLL_RATE, timer);
2279 	}
2280 }
2281 /**
2282  * Deletes the previous character. Delete the selected text if any.
2283  * Move the caret in front of the deleted text.
2284  */
doBackspace()2285 void doBackspace() {
2286 	Event event = new Event();
2287 	event.text = "";
2288 	if (selection.x != selection.y) {
2289 		event.start = selection.x;
2290 		event.end = selection.y;
2291 		sendKeyEvent(event);
2292 	} else if (caretOffset > 0) {
2293 		int lineIndex = content.getLineAtOffset(caretOffset);
2294 		int lineOffset = content.getOffsetAtLine(lineIndex);
2295 		if (caretOffset == lineOffset) {
2296 			lineOffset = content.getOffsetAtLine(lineIndex - 1);
2297 			event.start = lineOffset + content.getLine(lineIndex - 1).length();
2298 			event.end = caretOffset;
2299 		} else {
2300 			boolean isSurrogate = false;
2301 			String lineText = content.getLine(lineIndex);
2302 			char ch = lineText.charAt(caretOffset - lineOffset - 1);
2303 			if (0xDC00 <= ch && ch <= 0xDFFF) {
2304 				if (caretOffset - lineOffset - 2 >= 0) {
2305 					ch = lineText.charAt(caretOffset - lineOffset - 2);
2306 					isSurrogate = 0xD800 <= ch && ch <= 0xDBFF;
2307 				}
2308 			}
2309 			TextLayout layout = renderer.getTextLayout(lineIndex);
2310 			int start = layout.getPreviousOffset(caretOffset - lineOffset, isSurrogate ? SWT.MOVEMENT_CLUSTER : SWT.MOVEMENT_CHAR);
2311 			renderer.disposeTextLayout(layout);
2312 			event.start = start + lineOffset;
2313 			event.end = caretOffset;
2314 		}
2315 		sendKeyEvent(event);
2316 	}
2317 }
doBlockColumn(boolean next)2318 void doBlockColumn(boolean next) {
2319 	if (blockXLocation == -1) setBlockSelectionOffset(caretOffset, false);
2320 	int x = blockXLocation - horizontalScrollOffset;
2321 	int y = blockYLocation - getVerticalScrollOffset();
2322 	int[] trailing = new int[1];
2323 	int offset = getOffsetAtPoint(x, y, trailing, true);
2324 	if (offset != -1) {
2325 		offset += trailing[0];
2326 		int lineIndex = content.getLineAtOffset(offset);
2327 		int newOffset;
2328 		if (next) {
2329 			newOffset = getClusterNext(offset, lineIndex);
2330 		} else {
2331 			newOffset = getClusterPrevious(offset, lineIndex);
2332 		}
2333 		offset = newOffset != offset ? newOffset : -1;
2334 	}
2335 	if (offset != -1) {
2336 		setBlockSelectionOffset(offset, true);
2337 		showCaret();
2338 	} else {
2339 		int width = next ? renderer.averageCharWidth : -renderer.averageCharWidth;
2340 		int maxWidth = Math.max(clientAreaWidth - rightMargin - leftMargin, renderer.getWidth());
2341 		x = Math.max(0, Math.min(blockXLocation + width, maxWidth)) - horizontalScrollOffset;
2342 		setBlockSelectionLocation(x, y, true);
2343 		Rectangle rect = new Rectangle(x, y, 0, 0);
2344 		showLocation(rect, true);
2345 	}
2346 }
doBlockContentStartEnd(boolean end)2347 void doBlockContentStartEnd(boolean end) {
2348 	if (blockXLocation == -1) setBlockSelectionOffset(caretOffset, false);
2349 	int offset = end ? content.getCharCount() : 0;
2350 	setBlockSelectionOffset(offset, true);
2351 	showCaret();
2352 }
doBlockWord(boolean next)2353 void doBlockWord(boolean next) {
2354 	if (blockXLocation == -1) setBlockSelectionOffset(caretOffset, false);
2355 	int x = blockXLocation - horizontalScrollOffset;
2356 	int y = blockYLocation - getVerticalScrollOffset();
2357 	int[] trailing = new int[1];
2358 	int offset = getOffsetAtPoint(x, y, trailing, true);
2359 	if (offset != -1) {
2360 		offset += trailing[0];
2361 		int lineIndex = content.getLineAtOffset(offset);
2362 		int lineOffset = content.getOffsetAtLine(lineIndex);
2363 		String lineText = content.getLine(lineIndex);
2364 		int lineLength = lineText.length();
2365 		int newOffset = offset;
2366 		if (next) {
2367 			if (offset < lineOffset + lineLength) {
2368 				newOffset = getWordNext(offset, SWT.MOVEMENT_WORD);
2369 			}
2370 		} else {
2371 			if (offset > lineOffset) {
2372 				newOffset = getWordPrevious(offset, SWT.MOVEMENT_WORD);
2373 			}
2374 		}
2375 		offset = newOffset != offset ? newOffset : -1;
2376 	}
2377 	if (offset != -1) {
2378 		setBlockSelectionOffset(offset, true);
2379 		showCaret();
2380 	} else {
2381 		int width = (next ? renderer.averageCharWidth : -renderer.averageCharWidth) * 6;
2382 		int maxWidth = Math.max(clientAreaWidth - rightMargin - leftMargin, renderer.getWidth());
2383 		x = Math.max(0, Math.min(blockXLocation + width, maxWidth)) - horizontalScrollOffset;
2384 		setBlockSelectionLocation(x, y, true);
2385 		Rectangle rect = new Rectangle(x, y, 0, 0);
2386 		showLocation(rect, true);
2387 	}
2388 }
doBlockLineVertical(boolean up)2389 void doBlockLineVertical(boolean up) {
2390 	if (blockXLocation == -1) setBlockSelectionOffset(caretOffset, false);
2391 	int y = blockYLocation - getVerticalScrollOffset();
2392 	int lineIndex = getLineIndex(y);
2393 	if (up) {
2394 		if (lineIndex > 0) {
2395 			y = getLinePixel(lineIndex - 1);
2396 			setBlockSelectionLocation(blockXLocation - horizontalScrollOffset, y, true);
2397 			if (y < topMargin) {
2398 				scrollVertical(y - topMargin, true);
2399 			}
2400 		}
2401 	} else {
2402 		int lineCount = content.getLineCount();
2403 		if (lineIndex + 1 < lineCount) {
2404 			y = getLinePixel(lineIndex + 2) - 1;
2405 			setBlockSelectionLocation(blockXLocation - horizontalScrollOffset, y, true);
2406 			int bottom = clientAreaHeight - bottomMargin;
2407 			if (y > bottom) {
2408 				scrollVertical(y - bottom, true);
2409 			}
2410 		}
2411 	}
2412 }
doBlockLineHorizontal(boolean end)2413 void doBlockLineHorizontal(boolean end) {
2414 	if (blockXLocation == -1) setBlockSelectionOffset(caretOffset, false);
2415 	int x = blockXLocation - horizontalScrollOffset;
2416 	int y = blockYLocation - getVerticalScrollOffset();
2417 	int lineIndex = getLineIndex(y);
2418 	int lineOffset = content.getOffsetAtLine(lineIndex);
2419 	String lineText = content.getLine(lineIndex);
2420 	int lineLength = lineText.length();
2421 	int[] trailing = new int[1];
2422 	int offset = getOffsetAtPoint(x, y, trailing, true);
2423 	if (offset != -1) {
2424 		offset += trailing[0];
2425 		int newOffset = offset;
2426 		if (end) {
2427 			if (offset < lineOffset + lineLength) {
2428 				newOffset = lineOffset + lineLength;
2429 			}
2430 		} else {
2431 			if (offset > lineOffset) {
2432 				newOffset = lineOffset;
2433 			}
2434 		}
2435 		offset = newOffset != offset ? newOffset : -1;
2436 	} else {
2437 		if (!end) offset = lineOffset + lineLength;
2438 	}
2439 	if (offset != -1) {
2440 		setBlockSelectionOffset(offset, true);
2441 		showCaret();
2442 	} else {
2443 		int maxWidth = Math.max(clientAreaWidth - rightMargin - leftMargin, renderer.getWidth());
2444 		x = (end ? maxWidth : 0) - horizontalScrollOffset;
2445 		setBlockSelectionLocation(x, y, true);
2446 		Rectangle rect = new Rectangle(x, y, 0, 0);
2447 		showLocation(rect, true);
2448 	}
2449 }
doBlockSelection(boolean sendEvent)2450 void doBlockSelection(boolean sendEvent) {
2451 	if (caretOffset > selectionAnchor) {
2452 		selection.x = selectionAnchor;
2453 		selection.y = caretOffset;
2454 	} else {
2455 		selection.x = caretOffset;
2456 		selection.y = selectionAnchor;
2457 	}
2458 	updateCaretVisibility();
2459 	setCaretLocation();
2460 	super.redraw();
2461 	if (sendEvent) {
2462 		sendSelectionEvent();
2463 	}
2464 	sendAccessibleTextCaretMoved();
2465 }
2466 /**
2467  * Replaces the selection with the character or insert the character at the
2468  * current caret position if no selection exists.
2469  * <p>
2470  * If a carriage return was typed replace it with the line break character
2471  * used by the widget on this platform.
2472  * </p>
2473  *
2474  * @param key the character typed by the user
2475  */
doContent(char key)2476 void doContent(char key) {
2477 	if (blockSelection && blockXLocation != -1) {
2478 		insertBlockSelectionText(key, SWT.NULL);
2479 		return;
2480 	}
2481 
2482 	Event event = new Event();
2483 	event.start = selection.x;
2484 	event.end = selection.y;
2485 	// replace a CR line break with the widget line break
2486 	// CR does not make sense on Windows since most (all?) applications
2487 	// don't recognize CR as a line break.
2488 	if (key == SWT.CR || key == SWT.LF) {
2489 		if (!isSingleLine()) {
2490 			event.text = getLineDelimiter();
2491 		}
2492 	} else if (selection.x == selection.y && overwrite && key != TAB) {
2493 		// no selection and overwrite mode is on and the typed key is not a
2494 		// tab character (tabs are always inserted without overwriting)?
2495 		int lineIndex = content.getLineAtOffset(event.end);
2496 		int lineOffset = content.getOffsetAtLine(lineIndex);
2497 		String line = content.getLine(lineIndex);
2498 		// replace character at caret offset if the caret is not at the
2499 		// end of the line
2500 		if (event.end < lineOffset + line.length()) {
2501 			event.end++;
2502 		}
2503 		event.text = new String(new char[] {key});
2504 	} else {
2505 		event.text = new String(new char[] {key});
2506 	}
2507 	if (event.text != null) {
2508 		if (textLimit > 0 && content.getCharCount() - (event.end - event.start) >= textLimit) {
2509 			return;
2510 		}
2511 		sendKeyEvent(event);
2512 	}
2513 }
2514 /**
2515  * Moves the caret after the last character of the widget content.
2516  */
doContentEnd()2517 void doContentEnd() {
2518 	// place caret at end of first line if receiver is in single
2519 	// line mode. fixes 4820.
2520 	if (isSingleLine()) {
2521 		doLineEnd();
2522 	} else {
2523 		int length = content.getCharCount();
2524 		setCaretOffset(length, SWT.DEFAULT);
2525 		showCaret();
2526 	}
2527 }
2528 /**
2529  * Moves the caret in front of the first character of the widget content.
2530  */
doContentStart()2531 void doContentStart() {
2532 	setCaretOffset(0, SWT.DEFAULT);
2533 	showCaret();
2534 }
2535 /**
2536  * Moves the caret to the start of the selection if a selection exists.
2537  * Otherwise, if no selection exists move the cursor according to the
2538  * cursor selection rules.
2539  *
2540  * @see #doSelectionCursorPrevious
2541  */
doCursorPrevious()2542 void doCursorPrevious() {
2543 	if (selection.y - selection.x > 0) {
2544 		setCaretOffset(selection.x, OFFSET_LEADING);
2545 		showCaret();
2546 	} else {
2547 		doSelectionCursorPrevious();
2548 	}
2549 }
2550 /**
2551  * Moves the caret to the end of the selection if a selection exists.
2552  * Otherwise, if no selection exists move the cursor according to the
2553  * cursor selection rules.
2554  *
2555  * @see #doSelectionCursorNext
2556  */
doCursorNext()2557 void doCursorNext() {
2558 	if (selection.y - selection.x > 0) {
2559 		setCaretOffset(selection.y, PREVIOUS_OFFSET_TRAILING);
2560 		showCaret();
2561 	} else {
2562 		doSelectionCursorNext();
2563 	}
2564 }
2565 /**
2566  * Deletes the next character. Delete the selected text if any.
2567  */
doDelete()2568 void doDelete() {
2569 	Event event = new Event();
2570 	event.text = "";
2571 	if (selection.x != selection.y) {
2572 		event.start = selection.x;
2573 		event.end = selection.y;
2574 		sendKeyEvent(event);
2575 	} else if (caretOffset < content.getCharCount()) {
2576 		int line = content.getLineAtOffset(caretOffset);
2577 		int lineOffset = content.getOffsetAtLine(line);
2578 		int lineLength = content.getLine(line).length();
2579 		if (caretOffset == lineOffset + lineLength) {
2580 			event.start = caretOffset;
2581 			event.end = content.getOffsetAtLine(line + 1);
2582 		} else {
2583 			event.start = caretOffset;
2584 			event.end = getClusterNext(caretOffset, line);
2585 		}
2586 		sendKeyEvent(event);
2587 	}
2588 }
2589 /**
2590  * Deletes the next word.
2591  */
doDeleteWordNext()2592 void doDeleteWordNext() {
2593 	if (selection.x != selection.y) {
2594 		// if a selection exists, treat the as if
2595 		// only the delete key was pressed
2596 		doDelete();
2597 	} else {
2598 		Event event = new Event();
2599 		event.text = "";
2600 		event.start = caretOffset;
2601 		event.end = getWordNext(caretOffset, SWT.MOVEMENT_WORD);
2602 		sendKeyEvent(event);
2603 	}
2604 }
2605 /**
2606  * Deletes the previous word.
2607  */
doDeleteWordPrevious()2608 void doDeleteWordPrevious() {
2609 	if (selection.x != selection.y) {
2610 		// if a selection exists, treat as if
2611 		// only the backspace key was pressed
2612 		doBackspace();
2613 	} else {
2614 		Event event = new Event();
2615 		event.text = "";
2616 		event.start = getWordPrevious(caretOffset, SWT.MOVEMENT_WORD);
2617 		event.end = caretOffset;
2618 		sendKeyEvent(event);
2619 	}
2620 }
2621 /**
2622  * Moves the caret one line down and to the same character offset relative
2623  * to the beginning of the line. Move the caret to the end of the new line
2624  * if the new line is shorter than the character offset. Moves the caret to
2625  * the end of the text if the caret already is on the last line.
2626  */
doLineDown(boolean select)2627 void doLineDown(boolean select) {
2628 	int caretLine = getCaretLine();
2629 	int lineCount = content.getLineCount();
2630 	int y = 0;
2631 	boolean lastLine = false;
2632 	if (isWordWrap()) {
2633 		int lineOffset = content.getOffsetAtLine(caretLine);
2634 		int offsetInLine = caretOffset - lineOffset;
2635 		TextLayout layout = renderer.getTextLayout(caretLine);
2636 		int lineIndex = getVisualLineIndex(layout, offsetInLine);
2637 		int layoutLineCount = layout.getLineCount();
2638 		if (lineIndex == layoutLineCount - 1) {
2639 			lastLine = caretLine == lineCount - 1;
2640 			caretLine++;
2641 		} else {
2642 			y = layout.getLineBounds(lineIndex + 1).y;
2643 			y++; // bug 485722: workaround for fractional line heights
2644 		}
2645 		renderer.disposeTextLayout(layout);
2646 	} else {
2647 		lastLine = caretLine == lineCount - 1;
2648 		caretLine++;
2649 	}
2650 	if (lastLine) {
2651 		setCaretOffset(content.getCharCount(), SWT.DEFAULT);
2652 	} else {
2653 		int[] alignment = new int[1];
2654 		int offset = getOffsetAtPoint(columnX, y, caretLine, alignment);
2655 		setCaretOffset(offset, alignment[0]);
2656 	}
2657 	int oldColumnX = columnX;
2658 	int oldHScrollOffset = horizontalScrollOffset;
2659 	if (select) {
2660 		setMouseWordSelectionAnchor();
2661 		// select first and then scroll to reduce flash when key
2662 		// repeat scrolls lots of lines
2663 		doSelection(ST.COLUMN_NEXT);
2664 	}
2665 	showCaret();
2666 	int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
2667 	columnX = oldColumnX + hScrollChange;
2668 }
2669 /**
2670  * Moves the caret to the end of the line.
2671  */
doLineEnd()2672 void doLineEnd() {
2673 	int caretLine = getCaretLine();
2674 	int lineOffset = content.getOffsetAtLine(caretLine);
2675 	int lineEndOffset;
2676 	if (isWordWrap()) {
2677 		TextLayout layout = renderer.getTextLayout(caretLine);
2678 		int offsetInLine = caretOffset - lineOffset;
2679 		int lineIndex = getVisualLineIndex(layout, offsetInLine);
2680 		int[] offsets = layout.getLineOffsets();
2681 		lineEndOffset = lineOffset + offsets[lineIndex + 1];
2682 		renderer.disposeTextLayout(layout);
2683 	} else {
2684 		int lineLength = content.getLine(caretLine).length();
2685 		lineEndOffset = lineOffset + lineLength;
2686 	}
2687 	setCaretOffset(lineEndOffset, PREVIOUS_OFFSET_TRAILING);
2688 	showCaret();
2689 }
2690 /**
2691  * Moves the caret to the beginning of the line.
2692  */
doLineStart()2693 void doLineStart() {
2694 	int caretLine = getCaretLine();
2695 	int lineOffset = content.getOffsetAtLine(caretLine);
2696 	if (isWordWrap()) {
2697 		TextLayout layout = renderer.getTextLayout(caretLine);
2698 		int offsetInLine = caretOffset - lineOffset;
2699 		int lineIndex = getVisualLineIndex(layout, offsetInLine);
2700 		int[] offsets = layout.getLineOffsets();
2701 		lineOffset += offsets[lineIndex];
2702 		renderer.disposeTextLayout(layout);
2703 	}
2704 	setCaretOffset(lineOffset, OFFSET_LEADING);
2705 	showCaret();
2706 }
2707 /**
2708  * Moves the caret one line up and to the same character offset relative
2709  * to the beginning of the line. Move the caret to the end of the new line
2710  * if the new line is shorter than the character offset. Moves the caret to
2711  * the beginning of the document if it is already on the first line.
2712  */
doLineUp(boolean select)2713 void doLineUp(boolean select) {
2714 	int caretLine = getCaretLine(), y = 0;
2715 	boolean firstLine = false;
2716 	if (isWordWrap()) {
2717 		int lineOffset = content.getOffsetAtLine(caretLine);
2718 		int offsetInLine = caretOffset - lineOffset;
2719 		TextLayout layout = renderer.getTextLayout(caretLine);
2720 		int lineIndex = getVisualLineIndex(layout, offsetInLine);
2721 		if (lineIndex == 0) {
2722 			firstLine = caretLine == 0;
2723 			if (!firstLine) {
2724 				caretLine--;
2725 				y = renderer.getLineHeight(caretLine) - 1;
2726 				y--; // bug 485722: workaround for fractional line heights
2727 			}
2728 		} else {
2729 			y = layout.getLineBounds(lineIndex - 1).y;
2730 			y++; // bug 485722: workaround for fractional line heights
2731 		}
2732 		renderer.disposeTextLayout(layout);
2733 	} else {
2734 		firstLine = caretLine == 0;
2735 		caretLine--;
2736 	}
2737 	if (firstLine) {
2738 		setCaretOffset(0, SWT.DEFAULT);
2739 	} else {
2740 		int[] alignment = new int[1];
2741 		int offset = getOffsetAtPoint(columnX, y, caretLine, alignment);
2742 		setCaretOffset(offset, alignment[0]);
2743 	}
2744 	int oldColumnX = columnX;
2745 	int oldHScrollOffset = horizontalScrollOffset;
2746 	if (select) setMouseWordSelectionAnchor();
2747 	showCaret();
2748 	if (select) doSelection(ST.COLUMN_PREVIOUS);
2749 	int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
2750 	columnX = oldColumnX + hScrollChange;
2751 }
doMouseLinkCursor()2752 void doMouseLinkCursor() {
2753 	Display display = getDisplay();
2754 	Point point = display.getCursorLocation();
2755 	point = display.map(null, this, point);
2756 	doMouseLinkCursor(point.x, point.y);
2757 }
doMouseLinkCursor(int x, int y)2758 void doMouseLinkCursor(int x, int y) {
2759 	int offset = getOffsetAtPoint(x, y, null, true);
2760 	Display display = getDisplay();
2761 	Cursor newCursor = cursor;
2762 	if (renderer.hasLink(offset)) {
2763 		newCursor = display.getSystemCursor(SWT.CURSOR_HAND);
2764 	} else {
2765 		if (cursor == null) {
2766 			int type = blockSelection ? SWT.CURSOR_CROSS : SWT.CURSOR_IBEAM;
2767 			newCursor = display.getSystemCursor(type);
2768 		}
2769 	}
2770 	if (newCursor != getCursor()) super.setCursor(newCursor);
2771 }
2772 /**
2773  * Moves the caret to the specified location.
2774  *
2775  * @param x x location of the new caret position
2776  * @param y y location of the new caret position
2777  * @param select the location change is a selection operation.
2778  * 	include the line delimiter in the selection
2779  */
doMouseLocationChange(int x, int y, boolean select)2780 void doMouseLocationChange(int x, int y, boolean select) {
2781 	int line = getLineIndex(y);
2782 
2783 	updateCaretDirection = true;
2784 
2785 	if (blockSelection) {
2786 		x = Math.max(leftMargin, Math.min(x, clientAreaWidth - rightMargin));
2787 		y = Math.max(topMargin, Math.min(y, clientAreaHeight - bottomMargin));
2788 		if (doubleClickEnabled && clickCount > 1) {
2789 			boolean wordSelect = (clickCount & 1) == 0;
2790 			if (wordSelect) {
2791 				Point left = getPointAtOffset(doubleClickSelection.x);
2792 				int[] trailing = new int[1];
2793 				int offset = getOffsetAtPoint(x, y, trailing, true);
2794 				if (offset != -1) {
2795 					if (x > left.x) {
2796 						offset = getWordNext(offset + trailing[0], SWT.MOVEMENT_WORD_END);
2797 						setBlockSelectionOffset(doubleClickSelection.x, offset, true);
2798 					} else {
2799 						offset = getWordPrevious(offset + trailing[0], SWT.MOVEMENT_WORD_START);
2800 						setBlockSelectionOffset(doubleClickSelection.y, offset, true);
2801 					}
2802 				} else {
2803 					if (x > left.x) {
2804 						setBlockSelectionLocation(left.x, left.y, x, y, true);
2805 					} else {
2806 						Point right = getPointAtOffset(doubleClickSelection.y);
2807 						setBlockSelectionLocation(right.x, right.y, x, y, true);
2808 					}
2809 				}
2810 			} else {
2811 				setBlockSelectionLocation(blockXLocation, y, true);
2812 			}
2813 			return;
2814 		} else {
2815 			if (select) {
2816 				if (blockXLocation == -1) {
2817 					setBlockSelectionOffset(caretOffset, false);
2818 				}
2819 			} else {
2820 				clearBlockSelection(true, false);
2821 			}
2822 			int[] trailing = new int[1];
2823 			int offset = getOffsetAtPoint(x, y, trailing, true);
2824 			if (offset != -1) {
2825 				if (select) {
2826 					setBlockSelectionOffset(offset + trailing[0], true);
2827 					return;
2828 				}
2829 			} else {
2830 				if (isFixedLineHeight() && renderer.fixedPitch) {
2831 					int avg = renderer.averageCharWidth;
2832 					x = ((x + avg / 2 - leftMargin + horizontalScrollOffset) / avg * avg) + leftMargin - horizontalScrollOffset;
2833 				}
2834 				setBlockSelectionLocation(x, y, true);
2835 				return;
2836 			}
2837 		}
2838 	}
2839 
2840 	// allow caret to be placed below first line only if receiver is
2841 	// not in single line mode. fixes 4820.
2842 	if (line < 0 || (isSingleLine() && line > 0)) {
2843 		return;
2844 	}
2845 	int[] alignment = new int[1];
2846 	int newCaretOffset = getOffsetAtPoint(x, y, alignment);
2847 	int newCaretAlignemnt = alignment[0];
2848 
2849 	if (doubleClickEnabled && clickCount > 1) {
2850 		newCaretOffset = doMouseWordSelect(x, newCaretOffset, line);
2851 	}
2852 
2853 	int newCaretLine = content.getLineAtOffset(newCaretOffset);
2854 
2855 	// Is the mouse within the left client area border or on
2856 	// a different line? If not the autoscroll selection
2857 	// could be incorrectly reset. Fixes 1GKM3XS
2858 	boolean vchange = 0 <= y && y < clientAreaHeight || newCaretLine == 0 || newCaretLine == content.getLineCount() - 1;
2859 	boolean hchange = 0 <= x && x < clientAreaWidth || wordWrap || newCaretLine != content.getLineAtOffset(caretOffset);
2860 	if (vchange && hchange && (newCaretOffset != caretOffset || newCaretAlignemnt != caretAlignment)) {
2861 		setCaretOffset(newCaretOffset, newCaretAlignemnt);
2862 		if (select) doMouseSelection();
2863 		showCaret();
2864 	}
2865 	if (!select) {
2866 		setCaretOffset(newCaretOffset, newCaretAlignemnt);
2867 		clearSelection(true);
2868 	}
2869 }
2870 /**
2871  * Updates the selection based on the caret position
2872  */
2873 void doMouseSelection() {
2874 	if (caretOffset <= selection.x ||
2875 		(caretOffset > selection.x &&
2876 		 caretOffset < selection.y && selectionAnchor == selection.x)) {
2877 		doSelection(ST.COLUMN_PREVIOUS);
2878 	} else {
2879 		doSelection(ST.COLUMN_NEXT);
2880 	}
2881 }
2882 /**
2883  * Returns the offset of the word at the specified offset.
2884  * If the current selection extends from high index to low index
2885  * (i.e., right to left, or caret is at left border of selection on
2886  * non-bidi platforms) the start offset of the word preceding the
2887  * selection is returned. If the current selection extends from
2888  * low index to high index the end offset of the word following
2889  * the selection is returned.
2890  *
2891  * @param x mouse x location
2892  * @param newCaretOffset caret offset of the mouse cursor location
2893  * @param line line index of the mouse cursor location
2894  */
2895 int doMouseWordSelect(int x, int newCaretOffset, int line) {
2896 	// flip selection anchor based on word selection direction from
2897 	// base double click. Always do this here (and don't rely on doAutoScroll)
2898 	// because auto scroll only does not cover all possible mouse selections
2899 	// (e.g., mouse x < 0 && mouse y > caret line y)
2900 	if (newCaretOffset < selectionAnchor && selectionAnchor == selection.x) {
2901 		selectionAnchor = doubleClickSelection.y;
2902 	} else if (newCaretOffset > selectionAnchor && selectionAnchor == selection.y) {
2903 		selectionAnchor = doubleClickSelection.x;
2904 	}
2905 	if (0 <= x && x < clientAreaWidth) {
2906 		boolean wordSelect = (clickCount & 1) == 0;
2907 		if (caretOffset == selection.x) {
2908 			if (wordSelect) {
2909 				newCaretOffset = getWordPrevious(newCaretOffset, SWT.MOVEMENT_WORD_START);
2910 			} else {
2911 				newCaretOffset = content.getOffsetAtLine(line);
2912 			}
2913 		} else {
2914 			if (wordSelect) {
2915 				newCaretOffset = getWordNext(newCaretOffset, SWT.MOVEMENT_WORD_END);
2916 			} else {
2917 				int lineEnd = content.getCharCount();
2918 				if (line + 1 < content.getLineCount()) {
2919 					lineEnd = content.getOffsetAtLine(line + 1);
2920 				}
2921 				newCaretOffset = lineEnd;
2922 			}
2923 		}
2924 	}
2925 	return newCaretOffset;
2926 }
2927 /**
2928  * Scrolls one page down so that the last line (truncated or whole)
2929  * of the current page becomes the fully visible top line.
2930  * <p>
2931  * The caret is scrolled the same number of lines so that its location
2932  * relative to the top line remains the same. The exception is the end
2933  * of the text where a full page scroll is not possible. In this case
2934  * the caret is moved after the last character.
2935  * </p>
2936  *
2937  * @param select whether or not to select the page
2938  */
2939 void doPageDown(boolean select, int height) {
2940 	if (isSingleLine()) return;
2941 	int oldColumnX = columnX;
2942 	int oldHScrollOffset = horizontalScrollOffset;
2943 	if (isFixedLineHeight()) {
2944 		int lineCount = content.getLineCount();
2945 		int caretLine = getCaretLine();
2946 		if (caretLine < lineCount - 1) {
2947 			int lineHeight = renderer.getLineHeight();
2948 			int lines = (height == -1 ? clientAreaHeight : height) / lineHeight;
2949 			int scrollLines = Math.min(lineCount - caretLine - 1, lines);
2950 			// ensure that scrollLines never gets negative and at least one
2951 			// line is scrolled. fixes bug 5602.
2952 			scrollLines = Math.max(1, scrollLines);
2953 			int[] alignment = new int[1];
2954 			int offset = getOffsetAtPoint(columnX, getLinePixel(caretLine + scrollLines), alignment);
2955 			setCaretOffset(offset, alignment[0]);
2956 			if (select) {
2957 				doSelection(ST.COLUMN_NEXT);
2958 			}
2959 			// scroll one page down or to the bottom
2960 			int verticalMaximum = lineCount * getVerticalIncrement();
2961 			int pageSize = clientAreaHeight;
2962 			int verticalScrollOffset = getVerticalScrollOffset();
2963 			int scrollOffset = verticalScrollOffset + scrollLines * getVerticalIncrement();
2964 			if (scrollOffset + pageSize > verticalMaximum) {
2965 				scrollOffset = verticalMaximum - pageSize;
2966 			}
2967 			if (scrollOffset > verticalScrollOffset) {
2968 				scrollVertical(scrollOffset - verticalScrollOffset, true);
2969 			}
2970 		}
2971 	} else {
2972 		int lineCount = content.getLineCount();
2973 		int caretLine = getCaretLine();
2974 		int lineIndex, lineHeight;
2975 		if (height == -1) {
2976 			lineIndex = getPartialBottomIndex();
2977 			int topY = getLinePixel(lineIndex);
2978 			lineHeight = renderer.getLineHeight(lineIndex);
2979 			height = topY;
2980 			if (topY + lineHeight <= clientAreaHeight) {
2981 				height += lineHeight;
2982 			} else {
2983 				if (isWordWrap()) {
2984 					TextLayout layout = renderer.getTextLayout(lineIndex);
2985 					int y = clientAreaHeight - topY;
2986 					for (int i = 0; i < layout.getLineCount(); i++) {
2987 						Rectangle bounds = layout.getLineBounds(i);
2988 						if (bounds.contains(bounds.x, y)) {
2989 							height += bounds.y;
2990 							break;
2991 						}
2992 					}
2993 					renderer.disposeTextLayout(layout);
2994 				}
2995 			}
2996 		} else {
2997 			lineIndex = getLineIndex(height);
2998 			int topLineY = getLinePixel(lineIndex);
2999 			if (isWordWrap()) {
3000 				TextLayout layout = renderer.getTextLayout(lineIndex);
3001 				int y = height - topLineY;
3002 				for (int i = 0; i < layout.getLineCount(); i++) {
3003 					Rectangle bounds = layout.getLineBounds(i);
3004 					if (bounds.contains(bounds.x, y)) {
3005 						height = topLineY + bounds.y + bounds.height;
3006 						break;
3007 					}
3008 				}
3009 				renderer.disposeTextLayout(layout);
3010 			} else {
3011 				height = topLineY + renderer.getLineHeight(lineIndex);
3012 			}
3013 		}
3014 		int caretHeight = height;
3015 		if (isWordWrap()) {
3016 			TextLayout layout = renderer.getTextLayout(caretLine);
3017 			int offsetInLine = caretOffset - content.getOffsetAtLine(caretLine);
3018 			lineIndex = getVisualLineIndex(layout, offsetInLine);
3019 			caretHeight += layout.getLineBounds(lineIndex).y;
3020 			renderer.disposeTextLayout(layout);
3021 		}
3022 		lineIndex = caretLine;
3023 		lineHeight = renderer.getLineHeight(lineIndex);
3024 		while (caretHeight - lineHeight >= 0 && lineIndex < lineCount - 1) {
3025 			caretHeight -= lineHeight;
3026 			lineHeight = renderer.getLineHeight(++lineIndex);
3027 		}
3028 		int[] alignment = new int[1];
3029 		int offset = getOffsetAtPoint(columnX, caretHeight, lineIndex, alignment);
3030 		setCaretOffset(offset, alignment[0]);
3031 		if (select) doSelection(ST.COLUMN_NEXT);
3032 		height = getAvailableHeightBellow(height);
3033 		scrollVertical(height, true);
3034 		if (height == 0) setCaretLocation();
3035 	}
3036 	showCaret();
3037 	int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
3038 	columnX = oldColumnX + hScrollChange;
3039 }
3040 /**
3041  * Moves the cursor to the end of the last fully visible line.
3042  */
3043 void doPageEnd() {
3044 	// go to end of line if in single line mode. fixes 5673
3045 	if (isSingleLine()) {
3046 		doLineEnd();
3047 	} else {
3048 		int bottomOffset;
3049 		if (isWordWrap()) {
3050 			int lineIndex = getPartialBottomIndex();
3051 			TextLayout layout = renderer.getTextLayout(lineIndex);
3052 			int y = (clientAreaHeight - bottomMargin) - getLinePixel(lineIndex);
3053 			int index = layout.getLineCount() - 1;
3054 			while (index >= 0) {
3055 				Rectangle bounds = layout.getLineBounds(index);
3056 				if (y >= bounds.y + bounds.height) break;
3057 				index--;
3058 			}
3059 			if (index == -1 && lineIndex > 0) {
3060 				bottomOffset = content.getOffsetAtLine(lineIndex - 1) + content.getLine(lineIndex - 1).length();
3061 			} else {
3062 				bottomOffset = content.getOffsetAtLine(lineIndex) + Math.max(0, layout.getLineOffsets()[index + 1] - 1);
3063 			}
3064 			renderer.disposeTextLayout(layout);
3065 		} else {
3066 			int lineIndex = getBottomIndex();
3067 			bottomOffset = content.getOffsetAtLine(lineIndex) + content.getLine(lineIndex).length();
3068 		}
3069 		if (caretOffset < bottomOffset) {
3070 			setCaretOffset(bottomOffset, OFFSET_LEADING);
3071 			showCaret();
3072 		}
3073 	}
3074 }
3075 /**
3076  * Moves the cursor to the beginning of the first fully visible line.
3077  */
3078 void doPageStart() {
3079 	int topOffset;
3080 	if (isWordWrap()) {
3081 		int y, lineIndex;
3082 		if (topIndexY > 0) {
3083 			lineIndex = topIndex - 1;
3084 			y = renderer.getLineHeight(lineIndex) - topIndexY;
3085 		} else {
3086 			lineIndex = topIndex;
3087 			y = -topIndexY;
3088 		}
3089 		TextLayout layout = renderer.getTextLayout(lineIndex);
3090 		int index = 0;
3091 		int lineCount = layout.getLineCount();
3092 		while (index < lineCount) {
3093 			Rectangle bounds = layout.getLineBounds(index);
3094 			if (y <= bounds.y) break;
3095 			index++;
3096 		}
3097 		if (index == lineCount) {
3098 			topOffset = content.getOffsetAtLine(lineIndex + 1);
3099 		} else {
3100 			topOffset = content.getOffsetAtLine(lineIndex) + layout.getLineOffsets()[index];
3101 		}
3102 		renderer.disposeTextLayout(layout);
3103 	} else {
3104 		topOffset = content.getOffsetAtLine(topIndex);
3105 	}
3106 	if (caretOffset > topOffset) {
3107 		setCaretOffset(topOffset, OFFSET_LEADING);
3108 		showCaret();
3109 	}
3110 }
3111 /**
3112  * Scrolls one page up so that the first line (truncated or whole)
3113  * of the current page becomes the fully visible last line.
3114  * The caret is scrolled the same number of lines so that its location
3115  * relative to the top line remains the same. The exception is the beginning
3116  * of the text where a full page scroll is not possible. In this case the
3117  * caret is moved in front of the first character.
3118  */
3119 void doPageUp(boolean select, int height) {
3120 	if (isSingleLine()) return;
3121 	int oldHScrollOffset = horizontalScrollOffset;
3122 	int oldColumnX = columnX;
3123 	if (isFixedLineHeight()) {
3124 		int caretLine = getCaretLine();
3125 		if (caretLine > 0) {
3126 			int lineHeight = renderer.getLineHeight();
3127 			int lines = (height == -1 ? clientAreaHeight : height) / lineHeight;
3128 			int scrollLines = Math.max(1, Math.min(caretLine, lines));
3129 			caretLine -= scrollLines;
3130 			int[] alignment = new int[1];
3131 			int offset = getOffsetAtPoint(columnX, getLinePixel(caretLine), alignment);
3132 			setCaretOffset(offset, alignment[0]);
3133 			if (select) {
3134 				doSelection(ST.COLUMN_PREVIOUS);
3135 			}
3136 			int verticalScrollOffset = getVerticalScrollOffset();
3137 			int scrollOffset = Math.max(0, verticalScrollOffset - scrollLines * getVerticalIncrement());
3138 			if (scrollOffset < verticalScrollOffset) {
3139 				scrollVertical(scrollOffset - verticalScrollOffset, true);
3140 			}
3141 		}
3142 	} else {
3143 		int caretLine = getCaretLine();
3144 		int lineHeight, lineIndex;
3145 		if (height == -1) {
3146 			if (topIndexY == 0) {
3147 				height = clientAreaHeight;
3148 			} else {
3149 				int y;
3150 				if (topIndex > 0) {
3151 					lineIndex = topIndex - 1;
3152 					lineHeight = renderer.getLineHeight(lineIndex);
3153 					height = clientAreaHeight - topIndexY;
3154 					y = lineHeight - topIndexY;
3155 				} else {
3156 					lineIndex = topIndex;
3157 					lineHeight = renderer.getLineHeight(lineIndex);
3158 					height = clientAreaHeight - (lineHeight + topIndexY);
3159 					y = -topIndexY;
3160 				}
3161 				if (isWordWrap()) {
3162 					TextLayout layout = renderer.getTextLayout(lineIndex);
3163 					for (int i = 0; i < layout.getLineCount(); i++) {
3164 						Rectangle bounds = layout.getLineBounds(i);
3165 						if (bounds.contains(bounds.x, y)) {
3166 							height += lineHeight - (bounds.y + bounds.height);
3167 							break;
3168 						}
3169 					}
3170 					renderer.disposeTextLayout(layout);
3171 				}
3172 			}
3173 		} else {
3174 			lineIndex = getLineIndex(clientAreaHeight - height);
3175 			int topLineY = getLinePixel(lineIndex);
3176 			if (isWordWrap()) {
3177 				TextLayout layout = renderer.getTextLayout(lineIndex);
3178 				int y = topLineY;
3179 				for (int i = 0; i < layout.getLineCount(); i++) {
3180 					Rectangle bounds = layout.getLineBounds(i);
3181 					if (bounds.contains(bounds.x, y)) {
3182 						height = clientAreaHeight - (topLineY + bounds.y);
3183 						break;
3184 					}
3185 				}
3186 				renderer.disposeTextLayout(layout);
3187 			} else {
3188 				height = clientAreaHeight - topLineY;
3189 			}
3190 		}
3191 		int caretHeight = height;
3192 		if (isWordWrap()) {
3193 			TextLayout layout = renderer.getTextLayout(caretLine);
3194 			int offsetInLine = caretOffset - content.getOffsetAtLine(caretLine);
3195 			lineIndex = getVisualLineIndex(layout, offsetInLine);
3196 			caretHeight += layout.getBounds().height - layout.getLineBounds(lineIndex).y;
3197 			renderer.disposeTextLayout(layout);
3198 		}
3199 		lineIndex = caretLine;
3200 		lineHeight = renderer.getLineHeight(lineIndex);
3201 		while (caretHeight - lineHeight >= 0 && lineIndex > 0) {
3202 			caretHeight -= lineHeight;
3203 			lineHeight = renderer.getLineHeight(--lineIndex);
3204 		}
3205 		lineHeight = renderer.getLineHeight(lineIndex);
3206 		int[] alignment = new int[1];
3207 		int offset = getOffsetAtPoint(columnX, lineHeight - caretHeight, lineIndex, alignment);
3208 		setCaretOffset(offset, alignment[0]);
3209 		if (select) doSelection(ST.COLUMN_PREVIOUS);
3210 		height = getAvailableHeightAbove(height);
3211 		scrollVertical(-height, true);
3212 		if (height == 0) setCaretLocation();
3213 	}
3214 	showCaret();
3215 	int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
3216 	columnX = oldColumnX + hScrollChange;
3217 }
3218 /**
3219  * Updates the selection to extend to the current caret position.
3220  */
3221 void doSelection(int direction) {
3222 	int redrawStart = -1;
3223 	int redrawEnd = -1;
3224 	if (selectionAnchor == -1) {
3225 		selectionAnchor = selection.x;
3226 	}
3227 	if (direction == ST.COLUMN_PREVIOUS) {
3228 		if (caretOffset < selection.x) {
3229 			// grow selection
3230 			redrawEnd = selection.x;
3231 			redrawStart = selection.x = caretOffset;
3232 			// check if selection has reversed direction
3233 			if (selection.y != selectionAnchor) {
3234 				redrawEnd = selection.y;
3235 				selection.y = selectionAnchor;
3236 			}
3237 		// test whether selection actually changed. Fixes 1G71EO1
3238 		} else if (selectionAnchor == selection.x && caretOffset < selection.y) {
3239 			// caret moved towards selection anchor (left side of selection).
3240 			// shrink selection
3241 			redrawEnd = selection.y;
3242 			redrawStart = selection.y = caretOffset;
3243 		}
3244 	} else {
3245 		if (caretOffset > selection.y) {
3246 			// grow selection
3247 			redrawStart = selection.y;
3248 			redrawEnd = selection.y = caretOffset;
3249 			// check if selection has reversed direction
3250 			if (selection.x != selectionAnchor) {
3251 				redrawStart = selection.x;
3252 				selection.x = selectionAnchor;
3253 			}
3254 		// test whether selection actually changed. Fixes 1G71EO1
3255 		} else if (selectionAnchor == selection.y && caretOffset > selection.x) {
3256 			// caret moved towards selection anchor (right side of selection).
3257 			// shrink selection
3258 			redrawStart = selection.x;
3259 			redrawEnd = selection.x = caretOffset;
3260 		}
3261 	}
3262 	if (redrawStart != -1 && redrawEnd != -1) {
3263 		internalRedrawRange(redrawStart, redrawEnd - redrawStart);
3264 		sendSelectionEvent();
3265 	}
3266 	sendAccessibleTextCaretMoved();
3267 }
3268 /**
3269  * Moves the caret to the next character or to the beginning of the
3270  * next line if the cursor is at the end of a line.
3271  */
3272 void doSelectionCursorNext() {
3273 	int caretLine = getCaretLine();
3274 	int lineOffset = content.getOffsetAtLine(caretLine);
3275 	int offsetInLine = caretOffset - lineOffset;
3276 	int offset, alignment;
3277 	if (offsetInLine < content.getLine(caretLine).length()) {
3278 		TextLayout layout = renderer.getTextLayout(caretLine);
3279 		offsetInLine = layout.getNextOffset(offsetInLine, SWT.MOVEMENT_CLUSTER);
3280 		int lineStart = layout.getLineOffsets()[layout.getLineIndex(offsetInLine)];
3281 		renderer.disposeTextLayout(layout);
3282 		offset = offsetInLine + lineOffset;
3283 		alignment = offsetInLine == lineStart ? OFFSET_LEADING : PREVIOUS_OFFSET_TRAILING;
3284 		setCaretOffset(offset, alignment);
3285 		showCaret();
3286 	} else if (caretLine < content.getLineCount() - 1 && !isSingleLine()) {
3287 		caretLine++;
3288 		offset = content.getOffsetAtLine(caretLine);
3289 		alignment = PREVIOUS_OFFSET_TRAILING;
3290 		setCaretOffset(offset, alignment);
3291 		showCaret();
3292 	}
3293 }
3294 /**
3295  * Moves the caret to the previous character or to the end of the previous
3296  * line if the cursor is at the beginning of a line.
3297  */
3298 void doSelectionCursorPrevious() {
3299 	int caretLine = getCaretLine();
3300 	int lineOffset = content.getOffsetAtLine(caretLine);
3301 	int offsetInLine = caretOffset - lineOffset;
3302 	if (offsetInLine > 0) {
3303 		int offset = getClusterPrevious(caretOffset, caretLine);
3304 		setCaretOffset(offset, OFFSET_LEADING);
3305 		showCaret();
3306 	} else if (caretLine > 0) {
3307 		caretLine--;
3308 		lineOffset = content.getOffsetAtLine(caretLine);
3309 		int offset = lineOffset + content.getLine(caretLine).length();
3310 		setCaretOffset(offset, OFFSET_LEADING);
3311 		showCaret();
3312 	}
3313 }
3314 /**
3315  * Moves the caret one line down and to the same character offset relative
3316  * to the beginning of the line. Moves the caret to the end of the new line
3317  * if the new line is shorter than the character offset.
3318  * Moves the caret to the end of the text if the caret already is on the
3319  * last line.
3320  * Adjusts the selection according to the caret change. This can either add
3321  * to or subtract from the old selection, depending on the previous selection
3322  * direction.
3323  */
3324 void doSelectionLineDown() {
3325 	int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
3326 	doLineDown(true);
3327 	columnX = oldColumnX;
3328 }
3329 /**
3330  * Moves the caret one line up and to the same character offset relative
3331  * to the beginning of the line. Moves the caret to the end of the new line
3332  * if the new line is shorter than the character offset.
3333  * Moves the caret to the beginning of the document if it is already on the
3334  * first line.
3335  * Adjusts the selection according to the caret change. This can either add
3336  * to or subtract from the old selection, depending on the previous selection
3337  * direction.
3338  */
3339 void doSelectionLineUp() {
3340 	int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
3341 	doLineUp(true);
3342 	columnX = oldColumnX;
3343 }
3344 /**
3345  * Scrolls one page down so that the last line (truncated or whole)
3346  * of the current page becomes the fully visible top line.
3347  * <p>
3348  * The caret is scrolled the same number of lines so that its location
3349  * relative to the top line remains the same. The exception is the end
3350  * of the text where a full page scroll is not possible. In this case
3351  * the caret is moved after the last character.
3352  * </p><p>
3353  * Adjusts the selection according to the caret change. This can either add
3354  * to or subtract from the old selection, depending on the previous selection
3355  * direction.
3356  * </p>
3357  */
3358 void doSelectionPageDown(int pixels) {
3359 	int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
3360 	doPageDown(true, pixels);
3361 	columnX = oldColumnX;
3362 }
3363 /**
3364  * Scrolls one page up so that the first line (truncated or whole)
3365  * of the current page becomes the fully visible last line.
3366  * <p>
3367  * The caret is scrolled the same number of lines so that its location
3368  * relative to the top line remains the same. The exception is the beginning
3369  * of the text where a full page scroll is not possible. In this case the
3370  * caret is moved in front of the first character.
3371  * </p><p>
3372  * Adjusts the selection according to the caret change. This can either add
3373  * to or subtract from the old selection, depending on the previous selection
3374  * direction.
3375  * </p>
3376  */
3377 void doSelectionPageUp(int pixels) {
3378 	int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
3379 	doPageUp(true, pixels);
3380 	columnX = oldColumnX;
3381 }
3382 /**
3383  * Moves the caret to the end of the next word .
3384  */
3385 void doSelectionWordNext() {
3386 	int offset = getWordNext(caretOffset, SWT.MOVEMENT_WORD);
3387 	// don't change caret position if in single line mode and the cursor
3388 	// would be on a different line. fixes 5673
3389 	if (!isSingleLine() ||
3390 		content.getLineAtOffset(caretOffset) == content.getLineAtOffset(offset)) {
3391 		// Force symmetrical movement for word next and previous. Fixes 14536
3392 		setCaretOffset(offset, OFFSET_LEADING);
3393 		showCaret();
3394 	}
3395 }
3396 /**
3397  * Moves the caret to the start of the previous word.
3398  */
3399 void doSelectionWordPrevious() {
3400 	int offset = getWordPrevious(caretOffset, SWT.MOVEMENT_WORD);
3401 	setCaretOffset(offset, OFFSET_LEADING);
3402 	showCaret();
3403 }
3404 /**
3405  * Moves the caret one character to the left.  Do not go to the previous line.
3406  * When in a bidi locale and at a R2L character the caret is moved to the
3407  * beginning of the R2L segment (visually right) and then one character to the
3408  * left (visually left because it's now in a L2R segment).
3409  */
3410 void doVisualPrevious() {
3411 	int offset = getClusterPrevious(caretOffset, getCaretLine());
3412 	setCaretOffset(offset, SWT.DEFAULT);
3413 	showCaret();
3414 }
3415 /**
3416  * Moves the caret one character to the right.  Do not go to the next line.
3417  * When in a bidi locale and at a R2L character the caret is moved to the
3418  * end of the R2L segment (visually left) and then one character to the
3419  * right (visually right because it's now in a L2R segment).
3420  */
3421 void doVisualNext() {
3422 	int offset = getClusterNext(caretOffset, getCaretLine());
3423 	setCaretOffset(offset, SWT.DEFAULT);
3424 	showCaret();
3425 }
3426 /**
3427  * Moves the caret to the end of the next word.
3428  * If a selection exists, move the caret to the end of the selection
3429  * and remove the selection.
3430  */
3431 void doWordNext() {
3432 	if (selection.y - selection.x > 0) {
3433 		setCaretOffset(selection.y, SWT.DEFAULT);
3434 		showCaret();
3435 	} else {
3436 		doSelectionWordNext();
3437 	}
3438 }
3439 /**
3440  * Moves the caret to the start of the previous word.
3441  * If a selection exists, move the caret to the start of the selection
3442  * and remove the selection.
3443  */
3444 void doWordPrevious() {
3445 	if (selection.y - selection.x > 0) {
3446 		setCaretOffset(selection.x, SWT.DEFAULT);
3447 		showCaret();
3448 	} else {
3449 		doSelectionWordPrevious();
3450 	}
3451 }
3452 /**
3453  * Ends the autoscroll process.
3454  */
3455 void endAutoScroll() {
3456 	autoScrollDirection = SWT.NULL;
3457 }
3458 @Override
3459 public Color getBackground() {
3460 	checkWidget();
3461 	if (background == null) {
3462 		return getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
3463 	}
3464 	return background;
3465 }
3466 /**
3467  * Returns the baseline, in points.
3468  *
3469  * Note: this API should not be used if a StyleRange attribute causes lines to
3470  * have different heights (i.e. different fonts, rise, etc).
3471  *
3472  * @return baseline the baseline
3473  * @exception SWTException <ul>
3474  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3475  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3476  * </ul>
3477  * @since 3.0
3478  *
3479  * @see #getBaseline(int)
3480  */
3481 public int getBaseline() {
3482 	checkWidget();
3483 	return renderer.getBaseline();
3484 }
3485 /**
3486  * Returns the baseline at the given offset, in points.
3487  *
3488  * @param offset the offset
3489  *
3490  * @return baseline the baseline
3491  *
3492  * @exception SWTException <ul>
3493  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3494  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3495  * </ul>
3496  * @exception IllegalArgumentException <ul>
3497  *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (&lt; 0 or &gt; getCharCount())</li>
3498  * </ul>
3499  *
3500  * @since 3.2
3501  */
3502 public int getBaseline(int offset) {
3503 	checkWidget();
3504 	if (!(0 <= offset && offset <= content.getCharCount())) {
3505 		SWT.error(SWT.ERROR_INVALID_RANGE);
3506 	}
3507 	if (isFixedLineHeight()) {
3508 		return renderer.getBaseline();
3509 	}
3510 	int lineIndex = content.getLineAtOffset(offset);
3511 	int lineOffset = content.getOffsetAtLine(lineIndex);
3512 	TextLayout layout = renderer.getTextLayout(lineIndex);
3513 	int lineInParagraph = layout.getLineIndex(Math.min(offset - lineOffset, layout.getText().length()));
3514 	FontMetrics metrics = layout.getLineMetrics(lineInParagraph);
3515 	renderer.disposeTextLayout(layout);
3516 	return metrics.getAscent() + metrics.getLeading();
3517 }
3518 /**
3519  * Gets the BIDI coloring mode.  When true the BIDI text display
3520  * algorithm is applied to segments of text that are the same
3521  * color.
3522  *
3523  * @return the current coloring mode
3524  * @exception SWTException <ul>
3525  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3526  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3527  * </ul>
3528  *
3529  * @deprecated use BidiSegmentListener instead.
3530  */
3531 @Deprecated
3532 public boolean getBidiColoring() {
3533 	checkWidget();
3534 	return bidiColoring;
3535 }
3536 /**
3537  * Returns whether the widget is in block selection mode.
3538  *
3539  * @return true if widget is in block selection mode, false otherwise
3540  *
3541  * @exception SWTException <ul>
3542  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3543  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3544  * </ul>
3545  *
3546  * @since 3.5
3547  */
3548 public boolean getBlockSelection() {
3549 	checkWidget();
3550 	return blockSelection;
3551 }
3552 Rectangle getBlockSelectionPosition() {
3553 	int firstLine = getLineIndex(blockYAnchor - getVerticalScrollOffset());
3554 	int lastLine = getLineIndex(blockYLocation - getVerticalScrollOffset());
3555 	if (firstLine > lastLine) {
3556 		int temp = firstLine;
3557 		firstLine = lastLine;
3558 		lastLine = temp;
3559 	}
3560 	int left = blockXAnchor;
3561 	int right = blockXLocation;
3562 	if (left > right) {
3563 		left = blockXLocation;
3564 		right = blockXAnchor;
3565 	}
3566 	return new Rectangle (left - horizontalScrollOffset, firstLine, right - horizontalScrollOffset, lastLine);
3567 }
3568 /**
3569  * Returns the block selection bounds. The bounds is
3570  * relative to the upper left corner of the document.
3571  *
3572  * @return the block selection bounds
3573  *
3574  * @exception SWTException <ul>
3575  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3576  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3577  * </ul>
3578  *
3579  * @since 3.5
3580  */
3581 public Rectangle getBlockSelectionBounds() {
3582 	Rectangle rect;
3583 	if (blockSelection && blockXLocation != -1) {
3584 		rect = getBlockSelectionRectangle();
3585 	} else {
3586 		Point startPoint = getPointAtOffset(selection.x);
3587 		Point endPoint = getPointAtOffset(selection.y);
3588 		int height = getLineHeight(selection.y);
3589 		rect = new Rectangle(startPoint.x, startPoint.y, endPoint.x - startPoint.x, endPoint.y + height - startPoint.y);
3590 		if (selection.x == selection.y) {
3591 			rect.width = getCaretWidth();
3592 		}
3593 	}
3594 	rect.x += horizontalScrollOffset;
3595 	rect.y += getVerticalScrollOffset();
3596 	return rect;
3597 }
3598 Rectangle getBlockSelectionRectangle() {
3599 	Rectangle rect = getBlockSelectionPosition();
3600 	rect.y = getLinePixel(rect.y);
3601 	rect.width = rect.width - rect.x;
3602 	rect.height =  getLinePixel(rect.height + 1) - rect.y;
3603 	return rect;
3604 }
3605 String getBlockSelectionText(String delimiter) {
3606 	Rectangle rect = getBlockSelectionPosition();
3607 	int firstLine = rect.y;
3608 	int lastLine = rect.height;
3609 	int left = rect.x;
3610 	int right = rect.width;
3611 	StringBuilder buffer = new StringBuilder();
3612 	for (int lineIndex = firstLine; lineIndex <= lastLine; lineIndex++) {
3613 		int start = getOffsetAtPoint(left, 0, lineIndex, null);
3614 		int end = getOffsetAtPoint(right, 0, lineIndex, null);
3615 		if (start > end) {
3616 			int temp = start;
3617 			start = end;
3618 			end = temp;
3619 		}
3620 		String text = content.getTextRange(start, end - start);
3621 		buffer.append(text);
3622 		if (lineIndex < lastLine) buffer.append(delimiter);
3623 	}
3624 	return buffer.toString();
3625 }
3626 /**
3627  * Returns the index of the last fully visible line.
3628  *
3629  * @return index of the last fully visible line.
3630  */
3631 int getBottomIndex() {
3632 	int bottomIndex;
3633 	if (isFixedLineHeight()) {
3634 		int lineCount = 1;
3635 		int lineHeight = renderer.getLineHeight();
3636 		if (lineHeight != 0) {
3637 			// calculate the number of lines that are fully visible
3638 			int partialTopLineHeight = topIndex * lineHeight - getVerticalScrollOffset();
3639 			lineCount = (clientAreaHeight - partialTopLineHeight) / lineHeight;
3640 		}
3641 		bottomIndex = Math.min(content.getLineCount() - 1, topIndex + Math.max(0, lineCount - 1));
3642 	} else {
3643 		int clientAreaHeight = this.clientAreaHeight - bottomMargin;
3644 		bottomIndex = getLineIndex(clientAreaHeight);
3645 		if (bottomIndex > 0) {
3646 			int linePixel = getLinePixel(bottomIndex);
3647 			int lineHeight = renderer.getLineHeight(bottomIndex);
3648 			if (linePixel + lineHeight > clientAreaHeight) {
3649 				if (getLinePixel(bottomIndex - 1) >= topMargin) {
3650 					bottomIndex--;
3651 				}
3652 			}
3653 		}
3654 	}
3655 	return bottomIndex;
3656 }
3657 /**
3658  * Returns the bottom margin.
3659  *
3660  * @return the bottom margin.
3661  * @exception SWTException <ul>
3662  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3663  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3664  * </ul>
3665  *
3666  * @since 3.5
3667  */
3668 public int getBottomMargin() {
3669 	checkWidget();
3670 	return bottomMargin;
3671 }
3672 Rectangle getBoundsAtOffset(int offset) {
3673 	int lineIndex = content.getLineAtOffset(offset);
3674 	int lineOffset = content.getOffsetAtLine(lineIndex);
3675 	String line = content.getLine(lineIndex);
3676 	Rectangle bounds;
3677 	if (line.length() != 0) {
3678 		TextLayout layout = renderer.getTextLayout(lineIndex);
3679 		int offsetInLine = Math.min (layout.getText().length(), Math.max (0, offset - lineOffset));
3680 		bounds = layout.getBounds(offsetInLine, offsetInLine);
3681 		if (getListeners(ST.LineGetSegments).length > 0 && caretAlignment == PREVIOUS_OFFSET_TRAILING && offsetInLine != 0) {
3682 			offsetInLine = layout.getPreviousOffset(offsetInLine, SWT.MOVEMENT_CLUSTER);
3683 			Point point = layout.getLocation(offsetInLine, true);
3684 			bounds = new Rectangle (point.x, point.y, 0, bounds.height);
3685 		}
3686 		renderer.disposeTextLayout(layout);
3687 	} else {
3688 		bounds = new Rectangle (0, 0, 0, renderer.getLineHeight());
3689 	}
3690 	if (offset == caretOffset && !isWordWrap()) {
3691 		int lineEnd = lineOffset + line.length();
3692 		if (offset == lineEnd) {
3693 			bounds.width += getCaretWidth();
3694 		}
3695 	}
3696 	bounds.x += leftMargin - horizontalScrollOffset;
3697 	bounds.y += getLinePixel(lineIndex);
3698 	return bounds;
3699 }
3700 /**
3701  * Returns the caret position relative to the start of the text.
3702  *
3703  * @return the caret position relative to the start of the text.
3704  * @exception SWTException <ul>
3705  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3706  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3707  * </ul>
3708  */
3709 public int getCaretOffset() {
3710 	checkWidget();
3711 	return caretOffset;
3712 }
3713 /**
3714  * Returns the caret width.
3715  *
3716  * @return the caret width, 0 if caret is null.
3717  */
3718 int getCaretWidth() {
3719 	Caret caret = getCaret();
3720 	if (caret == null) return 0;
3721 	return caret.getSize().x;
3722 }
3723 Object getClipboardContent(int clipboardType) {
3724 	TextTransfer plainTextTransfer = TextTransfer.getInstance();
3725 	return clipboard.getContents(plainTextTransfer, clipboardType);
3726 }
3727 int getClusterNext(int offset, int lineIndex) {
3728 	int lineOffset = content.getOffsetAtLine(lineIndex);
3729 	TextLayout layout = renderer.getTextLayout(lineIndex);
3730 	offset -= lineOffset;
3731 	offset = layout.getNextOffset(offset, SWT.MOVEMENT_CLUSTER);
3732 	offset += lineOffset;
3733 	renderer.disposeTextLayout(layout);
3734 	return offset;
3735 }
3736 int getClusterPrevious(int offset, int lineIndex) {
3737 	int lineOffset = content.getOffsetAtLine(lineIndex);
3738 	TextLayout layout = renderer.getTextLayout(lineIndex);
3739 	offset -= lineOffset;
3740 	offset = layout.getPreviousOffset(offset, SWT.MOVEMENT_CLUSTER);
3741 	offset += lineOffset;
3742 	renderer.disposeTextLayout(layout);
3743 	return offset;
3744 }
3745 /**
3746  * Returns the content implementation that is used for text storage.
3747  *
3748  * @return content the user defined content implementation that is used for
3749  * text storage or the default content implementation if no user defined
3750  * content implementation has been set.
3751  * @exception SWTException <ul>
3752  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3753  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3754  * </ul>
3755  */
3756 public StyledTextContent getContent() {
3757 	checkWidget();
3758 	return content;
3759 }
3760 @Override
3761 public boolean getDragDetect () {
3762 	checkWidget ();
3763 	return dragDetect;
3764 }
3765 /**
3766  * Returns whether the widget implements double click mouse behavior.
3767  *
3768  * @return true if double clicking a word selects the word, false if double clicks
3769  * have the same effect as regular mouse clicks
3770  * @exception SWTException <ul>
3771  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3772  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3773  * </ul>
3774  */
3775 public boolean getDoubleClickEnabled() {
3776 	checkWidget();
3777 	return doubleClickEnabled;
3778 }
3779 /**
3780  * Returns whether the widget content can be edited.
3781  *
3782  * @return true if content can be edited, false otherwise
3783  * @exception SWTException <ul>
3784  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3785  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3786  * </ul>
3787  */
3788 public boolean getEditable() {
3789 	checkWidget();
3790 	return editable;
3791 }
3792 @Override
3793 public Color getForeground() {
3794 	checkWidget();
3795 	if (foreground == null) {
3796 		return getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND);
3797 	}
3798 	return foreground;
3799 }
3800 /**
3801  * Returns the horizontal scroll increment.
3802  *
3803  * @return horizontal scroll increment.
3804  */
3805 int getHorizontalIncrement() {
3806 	return renderer.averageCharWidth;
3807 }
3808 /**
3809  * Returns the horizontal scroll offset relative to the start of the line.
3810  *
3811  * @return horizontal scroll offset relative to the start of the line,
3812  * measured in character increments starting at 0, if &gt; 0 the content is scrolled
3813  * @exception SWTException <ul>
3814  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3815  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3816  * </ul>
3817  */
3818 public int getHorizontalIndex() {
3819 	checkWidget();
3820 	return horizontalScrollOffset / getHorizontalIncrement();
3821 }
3822 /**
3823  * Returns the horizontal scroll offset relative to the start of the line.
3824  *
3825  * @return the horizontal scroll offset relative to the start of the line,
3826  * measured in SWT logical point starting at 0, if &gt; 0 the content is scrolled.
3827  * @exception SWTException <ul>
3828  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3829  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3830  * </ul>
3831  */
3832 public int getHorizontalPixel() {
3833 	checkWidget();
3834 	return horizontalScrollOffset;
3835 }
3836 /**
3837  * Returns the line indentation of the widget.
3838  *
3839  * @return the line indentation
3840  *
3841  * @exception SWTException <ul>
3842  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3843  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3844  * </ul>
3845  *
3846  * @see #getLineIndent(int)
3847  *
3848  * @since 3.2
3849  */
3850 public int getIndent() {
3851 	checkWidget();
3852 	return indent;
3853 }
3854 /**
3855  * Returns whether the widget justifies lines.
3856  *
3857  * @return whether lines are justified
3858  *
3859  * @exception SWTException <ul>
3860  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3861  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3862  * </ul>
3863  *
3864  * @see #getLineJustify(int)
3865  *
3866  * @since 3.2
3867  */
3868 public boolean getJustify() {
3869 	checkWidget();
3870 	return justify;
3871 }
3872 /**
3873  * Returns the action assigned to the key.
3874  * Returns SWT.NULL if there is no action associated with the key.
3875  *
3876  * @param key a key code defined in SWT.java or a character.
3877  * 	Optionally ORd with a state mask.  Preferred state masks are one or more of
3878  *  SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform
3879  *  differences.  However, there may be cases where using the specific state masks
3880  *  (i.e., SWT.CTRL, SWT.SHIFT, SWT.ALT, SWT.COMMAND) makes sense.
3881  * @return one of the predefined actions defined in ST.java or SWT.NULL
3882  * 	if there is no action associated with the key.
3883  * @exception SWTException <ul>
3884  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3885  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3886  * </ul>
3887  */
3888 public int getKeyBinding(int key) {
3889 	checkWidget();
3890 	Integer action = keyActionMap.get(key);
3891 	return action == null ? SWT.NULL : action.intValue();
3892 }
3893 /**
3894  * Gets the number of characters.
3895  *
3896  * @return number of characters in the widget
3897  * @exception SWTException <ul>
3898  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3899  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3900  * </ul>
3901  */
3902 public int getCharCount() {
3903 	checkWidget();
3904 	return content.getCharCount();
3905 }
3906 /**
3907  * Returns the line at the given line index without delimiters.
3908  * Index 0 is the first line of the content. When there are not
3909  * any lines, getLine(0) is a valid call that answers an empty string.
3910  * <p>
3911  *
3912  * @param lineIndex index of the line to return.
3913  * @return the line text without delimiters
3914  *
3915  * @exception SWTException <ul>
3916  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3917  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3918  * </ul>
3919  * @exception IllegalArgumentException <ul>
3920  *   <li>ERROR_INVALID_RANGE when the line index is outside the valid range (&lt; 0 or &gt;= getLineCount())</li>
3921  * </ul>
3922  * @since 3.4
3923  */
3924 public String getLine(int lineIndex) {
3925 	checkWidget();
3926 	if (lineIndex < 0 ||
3927 		(lineIndex > 0 && lineIndex >= content.getLineCount())) {
3928 		SWT.error(SWT.ERROR_INVALID_RANGE);
3929 	}
3930 	return content.getLine(lineIndex);
3931 }
3932 /**
3933  * Returns the alignment of the line at the given index.
3934  *
3935  * @param index the index of the line
3936  *
3937  * @return the line alignment
3938  *
3939  * @exception SWTException <ul>
3940  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3941  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3942  * </ul>
3943  * @exception IllegalArgumentException <ul>
3944  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
3945  * </ul>
3946  *
3947  * @see #getAlignment()
3948  *
3949  * @since 3.2
3950  */
getLineAlignment(int index)3951 public int getLineAlignment(int index) {
3952 	checkWidget();
3953 	if (index < 0 || index > content.getLineCount()) {
3954 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3955 	}
3956 	return renderer.getLineAlignment(index, alignment);
3957 }
3958 /**
3959  * Returns the line at the specified offset in the text
3960  * where 0 &lt; offset &lt; getCharCount() so that getLineAtOffset(getCharCount())
3961  * returns the line of the insert location.
3962  *
3963  * @param offset offset relative to the start of the content.
3964  * 	0 &lt;= offset &lt;= getCharCount()
3965  * @return line at the specified offset in the text
3966  * @exception SWTException <ul>
3967  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3968  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3969  * </ul>
3970  * @exception IllegalArgumentException <ul>
3971  *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (&lt; 0 or &gt; getCharCount())</li>
3972  * </ul>
3973  */
getLineAtOffset(int offset)3974 public int getLineAtOffset(int offset) {
3975 	checkWidget();
3976 	if (offset < 0 || offset > getCharCount()) {
3977 		SWT.error(SWT.ERROR_INVALID_RANGE);
3978 	}
3979 	return content.getLineAtOffset(offset);
3980 }
3981 /**
3982  * Returns the background color of the line at the given index.
3983  * Returns null if a LineBackgroundListener has been set or if no background
3984  * color has been specified for the line. Should not be called if a
3985  * LineBackgroundListener has been set since the listener maintains the
3986  * line background colors.
3987  *
3988  * @param index the index of the line
3989  * @return the background color of the line at the given index.
3990  *
3991  * @exception SWTException <ul>
3992  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3993  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3994  * </ul>
3995  * @exception IllegalArgumentException <ul>
3996  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
3997  * </ul>
3998  */
getLineBackground(int index)3999 public Color getLineBackground(int index) {
4000 	checkWidget();
4001 	if (index < 0 || index > content.getLineCount()) {
4002 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4003 	}
4004 	return isListening(ST.LineGetBackground) ? null : renderer.getLineBackground(index, null);
4005 }
4006 /**
4007  * Returns the bullet of the line at the given index.
4008  *
4009  * @param index the index of the line
4010  *
4011  * @return the line bullet
4012  *
4013  * @exception SWTException <ul>
4014  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4015  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4016  * </ul>
4017  * @exception IllegalArgumentException <ul>
4018  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
4019  * </ul>
4020  *
4021  * @since 3.2
4022  */
getLineBullet(int index)4023 public Bullet getLineBullet(int index) {
4024 	checkWidget();
4025 	if (index < 0 || index > content.getLineCount()) {
4026 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4027 	}
4028 	return isListening(ST.LineGetStyle) ? null : renderer.getLineBullet(index, null);
4029 }
4030 /**
4031  * Returns the line background data for the given line or null if
4032  * there is none.
4033  *
4034  * @param lineOffset offset of the line start relative to the start
4035  * 	of the content.
4036  * @param line line to get line background data for
4037  * @return line background data for the given line.
4038  */
getLineBackgroundData(int lineOffset, String line)4039 StyledTextEvent getLineBackgroundData(int lineOffset, String line) {
4040 	return sendLineEvent(ST.LineGetBackground, lineOffset, line);
4041 }
4042 /**
4043  * Gets the number of text lines.
4044  *
4045  * @return the number of lines in the widget
4046  * @exception SWTException <ul>
4047  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4048  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4049  * </ul>
4050  */
getLineCount()4051 public int getLineCount() {
4052 	checkWidget();
4053 	return content.getLineCount();
4054 }
4055 /**
4056  * Returns the number of lines that can be completely displayed in the
4057  * widget client area.
4058  *
4059  * @return number of lines that can be completely displayed in the widget
4060  * 	client area.
4061  */
getLineCountWhole()4062 int getLineCountWhole() {
4063 	if (isFixedLineHeight()) {
4064 		int lineHeight = renderer.getLineHeight();
4065 		return lineHeight != 0 ? clientAreaHeight / lineHeight : 1;
4066 	}
4067 	return getBottomIndex() - topIndex + 1;
4068 }
4069 /**
4070  * Returns the line delimiter used for entering new lines by key down
4071  * or paste operation.
4072  *
4073  * @return line delimiter used for entering new lines by key down
4074  * or paste operation.
4075  * @exception SWTException <ul>
4076  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4077  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4078  * </ul>
4079  */
getLineDelimiter()4080 public String getLineDelimiter() {
4081 	checkWidget();
4082 	return content.getLineDelimiter();
4083 }
4084 /**
4085  * Returns the line height.
4086  * <p>
4087  * Note: this API should not be used if a StyleRange attribute causes lines to
4088  * have different heights (i.e. different fonts, rise, etc).
4089  * </p>
4090  *
4091  * @return line height in points.
4092  * @exception SWTException <ul>
4093  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4094  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4095  * </ul>
4096  * @see #getLineHeight(int)
4097  */
getLineHeight()4098 public int getLineHeight() {
4099 	checkWidget();
4100 	return renderer.getLineHeight();
4101 }
4102 /**
4103  * Returns the line height at the given offset.
4104  *
4105  * @param offset the offset
4106  *
4107  * @return line height in points
4108  *
4109  * @exception SWTException <ul>
4110  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4111  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4112  * </ul>
4113  * @exception IllegalArgumentException <ul>
4114  *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (&lt; 0 or &gt; getCharCount())</li>
4115  * </ul>
4116  *
4117  * @since 3.2
4118  */
getLineHeight(int offset)4119 public int getLineHeight(int offset) {
4120 	checkWidget();
4121 	if (!(0 <= offset && offset <= content.getCharCount())) {
4122 		SWT.error(SWT.ERROR_INVALID_RANGE);
4123 	}
4124 	if (isFixedLineHeight()) {
4125 		return renderer.getLineHeight();
4126 	}
4127 	int lineIndex = content.getLineAtOffset(offset);
4128 	int lineOffset = content.getOffsetAtLine(lineIndex);
4129 	TextLayout layout = renderer.getTextLayout(lineIndex);
4130 	int lineInParagraph = layout.getLineIndex(Math.min(offset - lineOffset, layout.getText().length()));
4131 	int height = layout.getLineBounds(lineInParagraph).height;
4132 	renderer.disposeTextLayout(layout);
4133 	return height;
4134 }
4135 /**
4136  * Returns the indentation of the line at the given index.
4137  *
4138  * @param index the index of the line
4139  *
4140  * @return the line indentation
4141  *
4142  * @exception SWTException <ul>
4143  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4144  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4145  * </ul>
4146  * @exception IllegalArgumentException <ul>
4147  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
4148  * </ul>
4149  *
4150  * @see #getIndent()
4151  *
4152  * @since 3.2
4153  */
getLineIndent(int index)4154 public int getLineIndent(int index) {
4155 	checkWidget();
4156 	if (index < 0 || index > content.getLineCount()) {
4157 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4158 	}
4159 	return isListening(ST.LineGetStyle) ? 0 : renderer.getLineIndent(index, indent);
4160 }
4161 /**
4162  * Returns the vertical indentation of the line at the given index.
4163  *
4164  * @param index the index of the line
4165  *
4166  * @return the line vertical indentation
4167  *
4168  * @exception SWTException <ul>
4169  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4170  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4171  * </ul>
4172  * @exception IllegalArgumentException <ul>
4173  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
4174  * </ul>
4175  *
4176  * @since 3.109
4177  */
getLineVerticalIndent(int index)4178 public int getLineVerticalIndent(int index) {
4179 	checkWidget();
4180 	if (index < 0 || index >= content.getLineCount()) {
4181 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4182 	}
4183 	return isListening(ST.LineGetStyle) ? 0 : renderer.getLineVerticalIndent(index);
4184 }
4185 /**
4186  * Returns whether the line at the given index is justified.
4187  *
4188  * @param index the index of the line
4189  *
4190  * @return whether the line is justified
4191  *
4192  * @exception SWTException <ul>
4193  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4194  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4195  * </ul>
4196  * @exception IllegalArgumentException <ul>
4197  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
4198  * </ul>
4199  *
4200  * @see #getJustify()
4201  *
4202  * @since 3.2
4203  */
getLineJustify(int index)4204 public boolean getLineJustify(int index) {
4205 	checkWidget();
4206 	if (index < 0 || index > content.getLineCount()) {
4207 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4208 	}
4209 	return isListening(ST.LineGetStyle) ? false : renderer.getLineJustify(index, justify);
4210 }
4211 /**
4212  * Returns the line spacing of the widget.
4213  *
4214  * @return the line spacing
4215  *
4216  * @exception SWTException <ul>
4217  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4218  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4219  * </ul>
4220  *
4221  * @since 3.2
4222  */
getLineSpacing()4223 public int getLineSpacing() {
4224 	checkWidget();
4225 	return lineSpacing;
4226 }
4227 /**
4228  * Returns the line style data for the given line or null if there is
4229  * none.
4230  * <p>
4231  * If there is a LineStyleListener but it does not set any styles,
4232  * the StyledTextEvent.styles field will be initialized to an empty
4233  * array.
4234  * </p>
4235  *
4236  * @param lineOffset offset of the line start relative to the start of
4237  * 	the content.
4238  * @param line line to get line styles for
4239  * @return line style data for the given line. Styles may start before
4240  * 	line start and end after line end
4241  */
getLineStyleData(int lineOffset, String line)4242 StyledTextEvent getLineStyleData(int lineOffset, String line) {
4243 	return sendLineEvent(ST.LineGetStyle, lineOffset, line);
4244 }
4245 /**
4246  * Returns the top SWT logical point, relative to the client area, of a given line.
4247  * Clamps out of ranges index.
4248  *
4249  * @param lineIndex the line index, the max value is lineCount. If
4250  * lineIndex == lineCount it returns the bottom SWT logical point of the last line.
4251  * It means this function can be used to retrieve the bottom SWT logical point of any line.
4252  *
4253  * @return the top SWT logical point of a given line index
4254  *
4255  * @since 3.2
4256  */
getLinePixel(int lineIndex)4257 public int getLinePixel(int lineIndex) {
4258 	checkWidget();
4259 	int lineCount = content.getLineCount();
4260 	lineIndex = Math.max(0, Math.min(lineCount, lineIndex));
4261 	if (isFixedLineHeight()) {
4262 		int lineHeight = renderer.getLineHeight();
4263 		return lineIndex * lineHeight - getVerticalScrollOffset() + topMargin;
4264 	}
4265 	if (lineIndex == topIndex) return topIndexY + topMargin;
4266 	int height = topIndexY;
4267 	if (lineIndex > topIndex) {
4268 		for (int i = topIndex; i < lineIndex; i++) {
4269 			height += renderer.getLineHeight(i);
4270 		}
4271 	} else {
4272 		for (int i = topIndex - 1; i >= lineIndex; i--) {
4273 			height -= renderer.getLineHeight(i);
4274 		}
4275 	}
4276 	return height + topMargin;
4277 }
4278 /**
4279  * Returns the line index for a y, relative to the client area.
4280  * The line index returned is always in the range 0..lineCount - 1.
4281  *
4282  * @param y the y-coordinate point
4283  *
4284  * @return the line index for a given y-coordinate point
4285  *
4286  * @since 3.2
4287  */
getLineIndex(int y)4288 public int getLineIndex(int y) {
4289 	checkWidget();
4290 	y -= topMargin;
4291 	if (isFixedLineHeight()) {
4292 		int lineHeight = renderer.getLineHeight();
4293 		int lineIndex = (y + getVerticalScrollOffset()) / lineHeight;
4294 		int lineCount = content.getLineCount();
4295 		lineIndex = Math.max(0, Math.min(lineCount - 1, lineIndex));
4296 		return lineIndex;
4297 	}
4298 	if (y == topIndexY) return topIndex;
4299 	int line = topIndex;
4300 	if (y < topIndexY) {
4301 		while (y < topIndexY && line > 0) {
4302 			y += renderer.getLineHeight(--line);
4303 		}
4304 	} else {
4305 		int lineCount = content.getLineCount();
4306 		int lineHeight = renderer.getLineHeight(line);
4307 		while (y - lineHeight >= topIndexY && line < lineCount - 1) {
4308 			y -= lineHeight;
4309 			lineHeight = renderer.getLineHeight(++line);
4310 		}
4311 	}
4312 	return line;
4313 }
4314 /**
4315  * Returns the tab stops of the line at the given <code>index</code>.
4316  *
4317  * @param index the index of the line
4318  *
4319  * @return the tab stops for the line
4320  *
4321  * @exception SWTException <ul>
4322  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4323  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4324  * </ul>
4325  * @exception IllegalArgumentException <ul>
4326  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
4327  * </ul>
4328  *
4329  * @see #getTabStops()
4330  *
4331  * @since 3.6
4332  */
getLineTabStops(int index)4333 public int[] getLineTabStops(int index) {
4334 	checkWidget();
4335 	if (index < 0 || index > content.getLineCount()) {
4336 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4337 	}
4338 	if (isListening(ST.LineGetStyle)) return null;
4339 	int[] tabs = renderer.getLineTabStops(index, null);
4340 	if (tabs == null) tabs = this.tabs;
4341 	if (tabs == null) return new int [] {renderer.tabWidth};
4342 	int[] result = new int[tabs.length];
4343 	System.arraycopy(tabs, 0, result, 0, tabs.length);
4344 	return result;
4345 }
4346 /**
4347  * Returns the wrap indentation of the line at the given <code>index</code>.
4348  *
4349  * @param index the index of the line
4350  *
4351  * @return the wrap indentation
4352  *
4353  * @exception SWTException <ul>
4354  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4355  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4356  * </ul>
4357  * @exception IllegalArgumentException <ul>
4358  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
4359  * </ul>
4360  *
4361  * @see #getWrapIndent()
4362  *
4363  * @since 3.6
4364  */
getLineWrapIndent(int index)4365 public int getLineWrapIndent(int index) {
4366 	checkWidget();
4367 	if (index < 0 || index > content.getLineCount()) {
4368 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4369 	}
4370 	return isListening(ST.LineGetStyle) ? 0 : renderer.getLineWrapIndent(index, wrapIndent);
4371 }
4372 /**
4373  * Returns the left margin.
4374  *
4375  * @return the left margin.
4376  * @exception SWTException <ul>
4377  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4378  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4379  * </ul>
4380  *
4381  * @since 3.5
4382  */
getLeftMargin()4383 public int getLeftMargin() {
4384 	checkWidget();
4385 	return leftMargin - alignmentMargin;
4386 }
4387 /**
4388  * Returns the x, y location of the upper left corner of the character
4389  * bounding box at the specified offset in the text. The point is
4390  * relative to the upper left corner of the widget client area.
4391  *
4392  * @param offset offset relative to the start of the content.
4393  * 	0 &lt;= offset &lt;= getCharCount()
4394  * @return x, y location of the upper left corner of the character
4395  * 	bounding box at the specified offset in the text.
4396  * @exception SWTException <ul>
4397  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4398  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4399  * </ul>
4400  * @exception IllegalArgumentException <ul>
4401  *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (&lt; 0 or &gt; getCharCount())</li>
4402  * </ul>
4403  */
getLocationAtOffset(int offset)4404 public Point getLocationAtOffset(int offset) {
4405 	checkWidget();
4406 	if (offset < 0 || offset > getCharCount()) {
4407 		SWT.error(SWT.ERROR_INVALID_RANGE);
4408 	}
4409 	return getPointAtOffset(offset);
4410 }
4411 /**
4412  * Returns <code>true</code> if the mouse navigator is enabled.
4413  * When mouse navigator is enabled, the user can navigate through the widget by pressing the middle button and moving the cursor
4414  *
4415  * @return the mouse navigator's enabled state
4416  *
4417  * @exception SWTException <ul>
4418  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4419  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4420  * </ul>
4421  *
4422  * @see #getEnabled
4423  * @since 3.110
4424  */
getMouseNavigatorEnabled()4425 public boolean getMouseNavigatorEnabled () {
4426 	checkWidget ();
4427 	return mouseNavigator != null;
4428 }
4429 /**
4430  * Returns the character offset of the first character of the given line.
4431  *
4432  * @param lineIndex index of the line, 0 based relative to the first
4433  * 	line in the content. 0 &lt;= lineIndex &lt; getLineCount(), except
4434  * 	lineIndex may always be 0
4435  * @return offset offset of the first character of the line, relative to
4436  * 	the beginning of the document. The first character of the document is
4437  *	at offset 0.
4438  *  When there are not any lines, getOffsetAtLine(0) is a valid call that
4439  * 	answers 0.
4440  * @exception SWTException <ul>
4441  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4442  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4443  * </ul>
4444  * @exception IllegalArgumentException <ul>
4445  *   <li>ERROR_INVALID_RANGE when the line index is outside the valid range (&lt; 0 or &gt;= getLineCount())</li>
4446  * </ul>
4447  * @since 2.0
4448  */
getOffsetAtLine(int lineIndex)4449 public int getOffsetAtLine(int lineIndex) {
4450 	checkWidget();
4451 	if (lineIndex < 0 ||
4452 		(lineIndex > 0 && lineIndex >= content.getLineCount())) {
4453 		SWT.error(SWT.ERROR_INVALID_RANGE);
4454 	}
4455 	return content.getOffsetAtLine(lineIndex);
4456 }
4457 /**
4458  * Returns the offset of the character at the given location relative
4459  * to the first character in the document.
4460  * <p>
4461  * The return value reflects the character offset that the caret will
4462  * be placed at if a mouse click occurred at the specified location.
4463  * If the x coordinate of the location is beyond the center of a character
4464  * the returned offset will be behind the character.
4465  * </p>
4466  *
4467  * @param point the origin of character bounding box relative to
4468  *  the origin of the widget client area.
4469  * @return offset of the character at the given location relative
4470  *  to the first character in the document.
4471  * @exception SWTException <ul>
4472  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4473  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4474  * </ul>
4475  * @exception IllegalArgumentException <ul>
4476  *   <li>ERROR_NULL_ARGUMENT when point is null</li>
4477  *   <li>ERROR_INVALID_ARGUMENT when there is no character at the specified location</li>
4478  * </ul>
4479  *
4480  * @deprecated Use {@link #getOffsetAtPoint(Point)} instead for better performance
4481  */
4482 @Deprecated
getOffsetAtLocation(Point point)4483 public int getOffsetAtLocation(Point point) {
4484 	checkWidget();
4485 	if (point == null) {
4486 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
4487 	}
4488 	int[] trailing = new int[1];
4489 	int offset = getOffsetAtPoint(point.x, point.y, trailing, true);
4490 	if (offset == -1) {
4491 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4492 	}
4493 	return offset + trailing[0];
4494 }
4495 
4496 /**
4497  * Returns the offset of the character at the given point relative
4498  * to the first character in the document.
4499  * <p>
4500  * The return value reflects the character offset that the caret will
4501  * be placed at if a mouse click occurred at the specified point.
4502  * If the x coordinate of the point is beyond the center of a character
4503  * the returned offset will be behind the character.
4504  * </p>
4505  * Note: This method is functionally similar to {@link #getOffsetAtLocation(Point)} except that
4506  * it does not throw an exception when no character is found and thus performs faster.
4507  *
4508  * @param point the origin of character bounding box relative to
4509  *  the origin of the widget client area.
4510  * @return offset of the character at the given point relative
4511  *  to the first character in the document.
4512  * -1 when there is no character at the specified location.
4513  * @exception SWTException <ul>
4514  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4515  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4516  * </ul>
4517  * @exception IllegalArgumentException <ul>
4518  *   <li>ERROR_NULL_ARGUMENT when point is <code>null</code></li>
4519  * </ul>
4520  *
4521  * @since 3.107
4522  */
getOffsetAtPoint(Point point)4523 public int getOffsetAtPoint(Point point) {
4524 	checkWidget();
4525 	if (point == null) {
4526 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
4527 	}
4528 	int[] trailing = new int[1];
4529 	int offset = getOffsetAtPoint(point.x, point.y, trailing, true);
4530 	return offset != -1 ? offset + trailing[0] : -1;
4531 }
4532 
getOffsetAtPoint(int x, int y, int[] alignment)4533 int getOffsetAtPoint(int x, int y, int[] alignment) {
4534 	int lineIndex = getLineIndex(y);
4535 	y -= getLinePixel(lineIndex);
4536 	return getOffsetAtPoint(x, y, lineIndex, alignment);
4537 }
getOffsetAtPoint(int x, int y, int lineIndex, int[] alignment)4538 int getOffsetAtPoint(int x, int y, int lineIndex, int[] alignment) {
4539 	TextLayout layout = renderer.getTextLayout(lineIndex);
4540 	x += horizontalScrollOffset - leftMargin;
4541 	int[] trailing = new int[1];
4542 	int offsetInLine = layout.getOffset(x, y, trailing);
4543 	if (alignment != null) alignment[0] = OFFSET_LEADING;
4544 	if (trailing[0] != 0) {
4545 		int lineInParagraph = layout.getLineIndex(offsetInLine + trailing[0]);
4546 		int lineStart = layout.getLineOffsets()[lineInParagraph];
4547 		if (offsetInLine + trailing[0] == lineStart) {
4548 			offsetInLine += trailing[0];
4549 			if (alignment != null) alignment[0] = PREVIOUS_OFFSET_TRAILING;
4550 		} else {
4551 			String line = content.getLine(lineIndex);
4552 			int level = 0;
4553 			if (alignment != null) {
4554 				int offset = offsetInLine;
4555 				while (offset > 0 && Character.isDigit(line.charAt(offset))) offset--;
4556 				if (offset == 0 && Character.isDigit(line.charAt(offset))) {
4557 					level = isMirrored() ? 1 : 0;
4558 				} else {
4559 					level = layout.getLevel(offset) & 0x1;
4560 				}
4561 			}
4562 			offsetInLine += trailing[0];
4563 			if (alignment != null) {
4564 				int trailingLevel = layout.getLevel(offsetInLine) & 0x1;
4565 				if (level != trailingLevel) {
4566 					alignment[0] = PREVIOUS_OFFSET_TRAILING;
4567 				} else {
4568 					alignment[0] = OFFSET_LEADING;
4569 				}
4570 			}
4571 		}
4572 	}
4573 	renderer.disposeTextLayout(layout);
4574 	return offsetInLine + content.getOffsetAtLine(lineIndex);
4575 }
getOffsetAtPoint(int x, int y, int[] trailing, boolean inTextOnly)4576 int getOffsetAtPoint(int x, int y, int[] trailing, boolean inTextOnly) {
4577 	if (inTextOnly && y + getVerticalScrollOffset() < 0 || x + horizontalScrollOffset < 0) {
4578 		return -1;
4579 	}
4580 	int bottomIndex = getPartialBottomIndex();
4581 	int height = getLinePixel(bottomIndex + 1);
4582 	if (inTextOnly && y > height) {
4583 		return -1;
4584 	}
4585 	int lineIndex = getLineIndex(y);
4586 	int lineOffset = content.getOffsetAtLine(lineIndex);
4587 	TextLayout layout = renderer.getTextLayout(lineIndex);
4588 	x += horizontalScrollOffset - leftMargin;
4589 	y -= getLinePixel(lineIndex);
4590 	int offset = layout.getOffset(x, y, trailing);
4591 	Rectangle rect = layout.getLineBounds(layout.getLineIndex(offset));
4592 	renderer.disposeTextLayout(layout);
4593 	if (inTextOnly && !(rect.x  <= x && x <=  rect.x + rect.width)) {
4594 		return -1;
4595 	}
4596 	return offset + lineOffset;
4597 }
4598 /**
4599  * Returns the orientation of the receiver.
4600  *
4601  * @return the orientation style
4602  *
4603  * @exception SWTException <ul>
4604  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4605  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4606  * </ul>
4607  *
4608  * @since 2.1.2
4609  */
4610 @Override
getOrientation()4611 public int getOrientation () {
4612 	return super.getOrientation ();
4613 }
4614 /**
4615  * Returns the index of the last partially visible line.
4616  *
4617  * @return index of the last partially visible line.
4618  */
getPartialBottomIndex()4619 int getPartialBottomIndex() {
4620 	if (isFixedLineHeight()) {
4621 		int lineHeight = renderer.getLineHeight();
4622 		int partialLineCount = Compatibility.ceil(clientAreaHeight, lineHeight);
4623 		return Math.max(0, Math.min(content.getLineCount(), topIndex + partialLineCount) - 1);
4624 	}
4625 	return getLineIndex(clientAreaHeight - bottomMargin);
4626 }
4627 /**
4628  * Returns the index of the first partially visible line.
4629  *
4630  * @return index of the first partially visible line.
4631  */
getPartialTopIndex()4632 int getPartialTopIndex() {
4633 	if (isFixedLineHeight()) {
4634 		int lineHeight = renderer.getLineHeight();
4635 		return getVerticalScrollOffset() / lineHeight;
4636 	}
4637 	return topIndexY <= 0 ? topIndex : topIndex - 1;
4638 }
4639 /**
4640  * Returns the content in the specified range using the platform line
4641  * delimiter to separate lines.
4642  *
4643  * @param writer the TextWriter to write line text into
4644  * @return the content in the specified range using the platform line
4645  * 	delimiter to separate lines as written by the specified TextWriter.
4646  */
getPlatformDelimitedText(TextWriter writer)4647 String getPlatformDelimitedText(TextWriter writer) {
4648 	int end = writer.getStart() + writer.getCharCount();
4649 	int startLine = content.getLineAtOffset(writer.getStart());
4650 	int endLine = content.getLineAtOffset(end);
4651 	String endLineText = content.getLine(endLine);
4652 	int endLineOffset = content.getOffsetAtLine(endLine);
4653 
4654 	for (int i = startLine; i <= endLine; i++) {
4655 		writer.writeLine(content.getLine(i), content.getOffsetAtLine(i));
4656 		if (i < endLine) {
4657 			writer.writeLineDelimiter(PlatformLineDelimiter);
4658 		}
4659 	}
4660 	if (end > endLineOffset + endLineText.length()) {
4661 		writer.writeLineDelimiter(PlatformLineDelimiter);
4662 	}
4663 	writer.close();
4664 	return writer.toString();
4665 }
4666 /**
4667  * Returns all the ranges of text that have an associated StyleRange.
4668  * Returns an empty array if a LineStyleListener has been set.
4669  * Should not be called if a LineStyleListener has been set since the
4670  * listener maintains the styles.
4671  * <p>
4672  * The ranges array contains start and length pairs.  Each pair refers to
4673  * the corresponding style in the styles array.  For example, the pair
4674  * that starts at ranges[n] with length ranges[n+1] uses the style
4675  * at styles[n/2] returned by <code>getStyleRanges(int, int, boolean)</code>.
4676  * </p>
4677  *
4678  * @return the ranges or an empty array if a LineStyleListener has been set.
4679  *
4680  * @exception SWTException <ul>
4681  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4682  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4683  * </ul>
4684  *
4685  * @since 3.2
4686  *
4687  * @see #getStyleRanges(boolean)
4688  */
getRanges()4689 public int[] getRanges() {
4690 	checkWidget();
4691 	if (!isListening(ST.LineGetStyle)) {
4692 		int[] ranges = renderer.getRanges(0, content.getCharCount());
4693 		if (ranges != null) return ranges;
4694 	}
4695 	return new int[0];
4696 }
4697 /**
4698  * Returns the ranges of text that have an associated StyleRange.
4699  * Returns an empty array if a LineStyleListener has been set.
4700  * Should not be called if a LineStyleListener has been set since the
4701  * listener maintains the styles.
4702  * <p>
4703  * The ranges array contains start and length pairs.  Each pair refers to
4704  * the corresponding style in the styles array.  For example, the pair
4705  * that starts at ranges[n] with length ranges[n+1] uses the style
4706  * at styles[n/2] returned by <code>getStyleRanges(int, int, boolean)</code>.
4707  * </p>
4708  *
4709  * @param start the start offset of the style ranges to return
4710  * @param length the number of style ranges to return
4711  *
4712  * @return the ranges or an empty array if a LineStyleListener has been set.
4713  *
4714  * @exception SWTException <ul>
4715  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4716  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4717  * </ul>
4718  * @exception IllegalArgumentException <ul>
4719  *   <li>ERROR_INVALID_RANGE if start or length are outside the widget content</li>
4720  * </ul>
4721  *
4722  * @since 3.2
4723  *
4724  * @see #getStyleRanges(int, int, boolean)
4725  */
getRanges(int start, int length)4726 public int[] getRanges(int start, int length) {
4727 	checkWidget();
4728 	int contentLength = getCharCount();
4729 	int end = start + length;
4730 	if (start > end || start < 0 || end > contentLength) {
4731 		SWT.error(SWT.ERROR_INVALID_RANGE);
4732 	}
4733 	if (!isListening(ST.LineGetStyle)) {
4734 		int[] ranges = renderer.getRanges(start, length);
4735 		if (ranges != null) return ranges;
4736 	}
4737 	return new int[0];
4738 }
4739 /**
4740  * Returns the right margin.
4741  *
4742  * @return the right margin.
4743  * @exception SWTException <ul>
4744  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4745  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4746  * </ul>
4747  *
4748  * @since 3.5
4749  */
getRightMargin()4750 public int getRightMargin() {
4751 	checkWidget();
4752 	return rightMargin;
4753 }
4754 /**
4755  * Returns the selection.
4756  * <p>
4757  * Text selections are specified in terms of caret positions.  In a text
4758  * widget that contains N characters, there are N+1 caret positions,
4759  * ranging from 0..N
4760  * </p>
4761  *
4762  * @return start and end of the selection, x is the offset of the first
4763  * 	selected character, y is the offset after the last selected character.
4764  *  The selection values returned are visual (i.e., x will always always be
4765  *  &lt;= y).  To determine if a selection is right-to-left (RtoL) vs. left-to-right
4766  *  (LtoR), compare the caretOffset to the start and end of the selection
4767  *  (e.g., caretOffset == start of selection implies that the selection is RtoL).
4768  * @see #getSelectionRange
4769  * @exception SWTException <ul>
4770  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4771  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4772  * </ul>
4773  */
getSelection()4774 public Point getSelection() {
4775 	checkWidget();
4776 	return new Point(selection.x, selection.y);
4777 }
4778 /**
4779  * Returns the selection.
4780  *
4781  * @return start and length of the selection, x is the offset of the
4782  * 	first selected character, relative to the first character of the
4783  * 	widget content. y is the length of the selection.
4784  *  The selection values returned are visual (i.e., length will always always be
4785  *  positive).  To determine if a selection is right-to-left (RtoL) vs. left-to-right
4786  *  (LtoR), compare the caretOffset to the start and end of the selection
4787  *  (e.g., caretOffset == start of selection implies that the selection is RtoL).
4788  * @exception SWTException <ul>
4789  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4790  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4791  * </ul>
4792  */
getSelectionRange()4793 public Point getSelectionRange() {
4794 	checkWidget();
4795 	return new Point(selection.x, selection.y - selection.x);
4796 }
4797 /**
4798  * Returns the ranges of text that are inside the block selection rectangle.
4799  * <p>
4800  * The ranges array contains start and length pairs. When the receiver is not
4801  * in block selection mode the return arrays contains the start and length of
4802  * the regular selection.
4803  *
4804  * @return the ranges array
4805  *
4806  * @exception SWTException <ul>
4807  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4808  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4809  * </ul>
4810  *
4811  * @since 3.5
4812  */
getSelectionRanges()4813 public int[] getSelectionRanges() {
4814 	checkWidget();
4815 	if (blockSelection && blockXLocation != -1) {
4816 		Rectangle rect = getBlockSelectionPosition();
4817 		int firstLine = rect.y;
4818 		int lastLine = rect.height;
4819 		int left = rect.x;
4820 		int right = rect.width;
4821 		int[] ranges = new int[(lastLine - firstLine + 1) * 2];
4822 		int index = 0;
4823 		for (int lineIndex = firstLine; lineIndex <= lastLine; lineIndex++) {
4824 			int start = getOffsetAtPoint(left, 0, lineIndex, null);
4825 			int end = getOffsetAtPoint(right, 0, lineIndex, null);
4826 			if (start > end) {
4827 				int temp = start;
4828 				start = end;
4829 				end = temp;
4830 			}
4831 			ranges[index++] = start;
4832 			ranges[index++] = end - start;
4833 		}
4834 		return ranges;
4835 	}
4836 	return new int[] {selection.x, selection.y - selection.x};
4837 }
4838 /**
4839  * Returns the receiver's selection background color.
4840  *
4841  * @return the selection background color
4842  *
4843  * @exception SWTException <ul>
4844  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4845  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4846  * </ul>
4847  * @since 2.1
4848  */
getSelectionBackground()4849 public Color getSelectionBackground() {
4850 	checkWidget();
4851 	if (selectionBackground == null) {
4852 		return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION);
4853 	}
4854 	return selectionBackground;
4855 }
4856 /**
4857  * Gets the number of selected characters.
4858  *
4859  * @return the number of selected characters.
4860  * @exception SWTException <ul>
4861  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4862  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4863  * </ul>
4864  */
getSelectionCount()4865 public int getSelectionCount() {
4866 	checkWidget();
4867 	if (blockSelection && blockXLocation != -1) {
4868 		return getBlockSelectionText(content.getLineDelimiter()).length();
4869 	}
4870 	return getSelectionRange().y;
4871 }
4872 /**
4873  * Returns the receiver's selection foreground color.
4874  *
4875  * @return the selection foreground color
4876  *
4877  * @exception SWTException <ul>
4878  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4879  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4880  * </ul>
4881  * @since 2.1
4882  */
getSelectionForeground()4883 public Color getSelectionForeground() {
4884 	checkWidget();
4885 	if (selectionForeground == null) {
4886 		return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT);
4887 	}
4888 	return selectionForeground;
4889 }
4890 /**
4891  * Returns the selected text.
4892  *
4893  * @return selected text, or an empty String if there is no selection.
4894  * @exception SWTException <ul>
4895  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4896  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4897  * </ul>
4898  */
getSelectionText()4899 public String getSelectionText() {
4900 	checkWidget();
4901 	if (blockSelection && blockXLocation != -1) {
4902 		return getBlockSelectionText(content.getLineDelimiter());
4903 	}
4904 	return content.getTextRange(selection.x, selection.y - selection.x);
4905 }
getBidiSegments(int lineOffset, String line)4906 StyledTextEvent getBidiSegments(int lineOffset, String line) {
4907 	if (!isListening(ST.LineGetSegments)) {
4908 		if (!bidiColoring) return null;
4909 		StyledTextEvent event = new StyledTextEvent(content);
4910 		event.segments = getBidiSegmentsCompatibility(line, lineOffset);
4911 		return event;
4912 	}
4913 	StyledTextEvent event = sendLineEvent(ST.LineGetSegments, lineOffset, line);
4914 	if (event == null || event.segments == null || event.segments.length == 0) return null;
4915 	int lineLength = line.length();
4916 	int[] segments = event.segments;
4917 	if (segments[0] > lineLength) {
4918 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4919 	}
4920 	char[] segmentsChars = event.segmentsChars;
4921 	boolean hasSegmentsChars = segmentsChars != null;
4922 	for (int i = 1; i < segments.length; i++) {
4923 		if ((hasSegmentsChars ? segments[i] < segments[i - 1] : segments[i] <= segments[i - 1]) || segments[i] > lineLength) {
4924 			SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4925 		}
4926 	}
4927 	if (hasSegmentsChars && !visualWrap) {
4928 		for (char segmentsChar : segmentsChars) {
4929 			if (segmentsChar == '\n' || segmentsChar == '\r') {
4930 				visualWrap = true;
4931 				break;
4932 			}
4933 		}
4934 	}
4935 	return event;
4936 }
4937 /**
4938  * @see #getBidiSegments
4939  * Supports deprecated setBidiColoring API. Remove when API is removed.
4940  */
getBidiSegmentsCompatibility(String line, int lineOffset)4941 int [] getBidiSegmentsCompatibility(String line, int lineOffset) {
4942 	int lineLength = line.length();
4943 	StyleRange [] styles = null;
4944 	StyledTextEvent event = getLineStyleData(lineOffset, line);
4945 	if (event != null) {
4946 		styles = event.styles;
4947 	} else {
4948 		styles = renderer.getStyleRanges(lineOffset, lineLength, true);
4949 	}
4950 	if (styles == null || styles.length == 0) {
4951 		return new int[] {0, lineLength};
4952 	}
4953 	int k=0, count = 1;
4954 	while (k < styles.length && styles[k].start == 0 && styles[k].length == lineLength) {
4955 		k++;
4956 	}
4957 	int[] offsets = new int[(styles.length - k) * 2 + 2];
4958 	for (int i = k; i < styles.length; i++) {
4959 		StyleRange style = styles[i];
4960 		int styleLineStart = Math.max(style.start - lineOffset, 0);
4961 		int styleLineEnd = Math.max(style.start + style.length - lineOffset, styleLineStart);
4962 		styleLineEnd = Math.min (styleLineEnd, line.length ());
4963 		if (i > 0 && count > 1 &&
4964 			((styleLineStart >= offsets[count-2] && styleLineStart <= offsets[count-1]) ||
4965 			 (styleLineEnd >= offsets[count-2] && styleLineEnd <= offsets[count-1])) &&
4966 			 style.similarTo(styles[i-1])) {
4967 			offsets[count-2] = Math.min(offsets[count-2], styleLineStart);
4968 			offsets[count-1] = Math.max(offsets[count-1], styleLineEnd);
4969 		} else {
4970 			if (styleLineStart > offsets[count - 1]) {
4971 				offsets[count] = styleLineStart;
4972 				count++;
4973 			}
4974 			offsets[count] = styleLineEnd;
4975 			count++;
4976 		}
4977 	}
4978 	// add offset for last non-colored segment in line, if any
4979 	if (lineLength > offsets[count-1]) {
4980 		offsets [count] = lineLength;
4981 		count++;
4982 	}
4983 	if (count == offsets.length) {
4984 		return offsets;
4985 	}
4986 	int [] result = new int [count];
4987 	System.arraycopy (offsets, 0, result, 0, count);
4988 	return result;
4989 }
4990 /**
4991  * Returns the style range at the given offset.
4992  * <p>
4993  * Returns null if a LineStyleListener has been set or if a style is not set
4994  * for the offset.
4995  * Should not be called if a LineStyleListener has been set since the
4996  * listener maintains the styles.
4997  * </p>
4998  *
4999  * @param offset the offset to return the style for.
5000  * 	0 &lt;= offset &lt; getCharCount() must be true.
5001  * @return a StyleRange with start == offset and length == 1, indicating
5002  * 	the style at the given offset. null if a LineStyleListener has been set
5003  * 	or if a style is not set for the given offset.
5004  * @exception SWTException <ul>
5005  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5006  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5007  * </ul>
5008  * @exception IllegalArgumentException <ul>
5009  *   <li>ERROR_INVALID_ARGUMENT when the offset is invalid</li>
5010  * </ul>
5011  */
getStyleRangeAtOffset(int offset)5012 public StyleRange getStyleRangeAtOffset(int offset) {
5013 	checkWidget();
5014 	if (offset < 0 || offset >= getCharCount()) {
5015 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
5016 	}
5017 	if (!isListening(ST.LineGetStyle)) {
5018 		StyleRange[] ranges = renderer.getStyleRanges(offset, 1, true);
5019 		if (ranges != null) return ranges[0];
5020 	}
5021 	return null;
5022 }
5023 /**
5024  * Returns the styles.
5025  * <p>
5026  * Returns an empty array if a LineStyleListener has been set.
5027  * Should not be called if a LineStyleListener has been set since the
5028  * listener maintains the styles.
5029  * </p><p>
5030  * Note: Because a StyleRange includes the start and length, the
5031  * same instance cannot occur multiple times in the array of styles.
5032  * If the same style attributes, such as font and color, occur in
5033  * multiple StyleRanges, <code>getStyleRanges(boolean)</code>
5034  * can be used to get the styles without the ranges.
5035  * </p>
5036  *
5037  * @return the styles or an empty array if a LineStyleListener has been set.
5038  *
5039  * @exception SWTException <ul>
5040  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5041  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5042  * </ul>
5043  *
5044  * @see #getStyleRanges(boolean)
5045  */
getStyleRanges()5046 public StyleRange[] getStyleRanges() {
5047 	checkWidget();
5048 	return getStyleRanges(0, content.getCharCount(), true);
5049 }
5050 /**
5051  * Returns the styles.
5052  * <p>
5053  * Returns an empty array if a LineStyleListener has been set.
5054  * Should not be called if a LineStyleListener has been set since the
5055  * listener maintains the styles.
5056  * </p><p>
5057  * Note: When <code>includeRanges</code> is true, the start and length
5058  * fields of each StyleRange will be valid, however the StyleRange
5059  * objects may need to be cloned. When <code>includeRanges</code> is
5060  * false, <code>getRanges(int, int)</code> can be used to get the
5061  * associated ranges.
5062  * </p>
5063  *
5064  * @param includeRanges whether the start and length field of the StyleRanges should be set.
5065  *
5066  * @return the styles or an empty array if a LineStyleListener has been set.
5067  *
5068  * @exception SWTException <ul>
5069  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5070  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5071  * </ul>
5072  *
5073  * @since 3.2
5074  *
5075  * @see #getRanges(int, int)
5076  * @see #setStyleRanges(int[], StyleRange[])
5077  */
getStyleRanges(boolean includeRanges)5078 public StyleRange[] getStyleRanges(boolean includeRanges) {
5079 	checkWidget();
5080 	return getStyleRanges(0, content.getCharCount(), includeRanges);
5081 }
5082 /**
5083  * Returns the styles for the given text range.
5084  * <p>
5085  * Returns an empty array if a LineStyleListener has been set.
5086  * Should not be called if a LineStyleListener has been set since the
5087  * listener maintains the styles.
5088  * </p><p>
5089  * Note: Because the StyleRange includes the start and length, the
5090  * same instance cannot occur multiple times in the array of styles.
5091  * If the same style attributes, such as font and color, occur in
5092  * multiple StyleRanges, <code>getStyleRanges(int, int, boolean)</code>
5093  * can be used to get the styles without the ranges.
5094  * </p>
5095  * @param start the start offset of the style ranges to return
5096  * @param length the number of style ranges to return
5097  *
5098  * @return the styles or an empty array if a LineStyleListener has
5099  *  been set.  The returned styles will reflect the given range.  The first
5100  *  returned <code>StyleRange</code> will have a starting offset &gt;= start
5101  *  and the last returned <code>StyleRange</code> will have an ending
5102  *  offset &lt;= start + length - 1
5103  *
5104  * @exception SWTException <ul>
5105  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5106  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5107  * </ul>
5108  * @exception IllegalArgumentException <ul>
5109  *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
5110  * </ul>
5111  *
5112  * @see #getStyleRanges(int, int, boolean)
5113  *
5114  * @since 3.0
5115  */
getStyleRanges(int start, int length)5116 public StyleRange[] getStyleRanges(int start, int length) {
5117 	checkWidget();
5118 	return getStyleRanges(start, length, true);
5119 }
5120 /**
5121  * Returns the styles for the given text range.
5122  * <p>
5123  * Returns an empty array if a LineStyleListener has been set.
5124  * Should not be called if a LineStyleListener has been set since the
5125  * listener maintains the styles.
5126  * </p><p>
5127  * Note: When <code>includeRanges</code> is true, the start and length
5128  * fields of each StyleRange will be valid, however the StyleRange
5129  * objects may need to be cloned. When <code>includeRanges</code> is
5130  * false, <code>getRanges(int, int)</code> can be used to get the
5131  * associated ranges.
5132  * </p>
5133  *
5134  * @param start the start offset of the style ranges to return
5135  * @param length the number of style ranges to return
5136  * @param includeRanges whether the start and length field of the StyleRanges should be set.
5137  *
5138  * @return the styles or an empty array if a LineStyleListener has
5139  *  been set.  The returned styles will reflect the given range.  The first
5140  *  returned <code>StyleRange</code> will have a starting offset &gt;= start
5141  *  and the last returned <code>StyleRange</code> will have an ending
5142  *  offset &gt;= start + length - 1
5143  *
5144  * @exception SWTException <ul>
5145  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5146  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5147  * </ul>
5148  * @exception IllegalArgumentException <ul>
5149  *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
5150  * </ul>
5151  *
5152  * @since 3.2
5153  *
5154  * @see #getRanges(int, int)
5155  * @see #setStyleRanges(int[], StyleRange[])
5156  */
getStyleRanges(int start, int length, boolean includeRanges)5157 public StyleRange[] getStyleRanges(int start, int length, boolean includeRanges) {
5158 	checkWidget();
5159 	int contentLength = getCharCount();
5160 	int end = start + length;
5161 	if (start > end || start < 0 || end > contentLength) {
5162 		SWT.error(SWT.ERROR_INVALID_RANGE);
5163 	}
5164 	if (!isListening(ST.LineGetStyle)) {
5165 		StyleRange[] ranges = renderer.getStyleRanges(start, length, includeRanges);
5166 		if (ranges != null) return ranges;
5167 	}
5168 	return new StyleRange[0];
5169 }
5170 /**
5171  * Returns the tab width measured in characters.
5172  *
5173  * @return tab width measured in characters
5174  * @exception SWTException <ul>
5175  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5176  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5177  * </ul>
5178  *
5179  * @see #getTabStops()
5180  */
getTabs()5181 public int getTabs() {
5182 	checkWidget();
5183 	return tabLength;
5184 }
5185 
5186 /**
5187  * Returns the tab list of the receiver.
5188  *
5189  * @return the tab list
5190  * @exception SWTException <ul>
5191  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5192  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5193  * </ul>
5194  *
5195  * @since 3.6
5196  */
getTabStops()5197 public int[] getTabStops() {
5198 	checkWidget();
5199 	if (tabs == null) return new int [] {renderer.tabWidth};
5200 	int[] result = new int[tabs.length];
5201 	System.arraycopy(tabs, 0, result, 0, tabs.length);
5202 	return result;
5203 }
5204 
5205 /**
5206  * Returns a copy of the widget content.
5207  *
5208  * @return copy of the widget content
5209  * @exception SWTException <ul>
5210  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5211  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5212  * </ul>
5213  */
getText()5214 public String getText() {
5215 	checkWidget();
5216 	return content.getTextRange(0, getCharCount());
5217 }
5218 /**
5219  * Returns the widget content between the two offsets.
5220  *
5221  * @param start offset of the first character in the returned String
5222  * @param end offset of the last character in the returned String
5223  * @return widget content starting at start and ending at end
5224  * @see #getTextRange(int,int)
5225  * @exception SWTException <ul>
5226  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5227  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5228  * </ul>
5229  * @exception IllegalArgumentException <ul>
5230  *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
5231  * </ul>
5232  */
getText(int start, int end)5233 public String getText(int start, int end) {
5234 	checkWidget();
5235 	int contentLength = getCharCount();
5236 	if (start < 0 || start >= contentLength || end < 0 || end >= contentLength || start > end) {
5237 		SWT.error(SWT.ERROR_INVALID_RANGE);
5238 	}
5239 	return content.getTextRange(start, end - start + 1);
5240 }
5241 /**
5242  * Returns the smallest bounding rectangle that includes the characters between two offsets.
5243  *
5244  * @param start offset of the first character included in the bounding box
5245  * @param end offset of the last character included in the bounding box
5246  * @return bounding box of the text between start and end
5247  * @exception SWTException <ul>
5248  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5249  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5250  * </ul>
5251  * @exception IllegalArgumentException <ul>
5252  *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
5253  * </ul>
5254  * @since 3.1
5255  */
getTextBounds(int start, int end)5256 public Rectangle getTextBounds(int start, int end) {
5257 	checkWidget();
5258 	int contentLength = getCharCount();
5259 	if (start < 0 || start >= contentLength || end < 0 || end >= contentLength || start > end) {
5260 		SWT.error(SWT.ERROR_INVALID_RANGE);
5261 	}
5262 	int lineStart = content.getLineAtOffset(start);
5263 	int lineEnd = content.getLineAtOffset(end);
5264 	Rectangle rect;
5265 	int y = getLinePixel(lineStart);
5266 	int height = 0;
5267 	int left = 0x7fffffff, right = 0;
5268 	for (int i = lineStart; i <= lineEnd; i++) {
5269 		int lineOffset = content.getOffsetAtLine(i);
5270 		TextLayout layout = renderer.getTextLayout(i);
5271 		int length = layout.getText().length();
5272 		if (length > 0) {
5273 			if (i == lineStart) {
5274 				if (i == lineEnd) {
5275 					rect = layout.getBounds(start - lineOffset, end - lineOffset);
5276 				} else {
5277 					rect = layout.getBounds(start - lineOffset, length);
5278 				}
5279 				y += rect.y;
5280 			} else if (i == lineEnd) {
5281 				rect = layout.getBounds(0, end - lineOffset);
5282 			} else {
5283 				rect = layout.getBounds();
5284 			}
5285 			left = Math.min(left, rect.x);
5286 			right = Math.max(right, rect.x + rect.width);
5287 			height += rect.height;
5288 		} else {
5289 			height += renderer.getLineHeight();
5290 		}
5291 		renderer.disposeTextLayout(layout);
5292 	}
5293 	rect = new Rectangle (left, y, right-left, height);
5294 	rect.x += leftMargin - horizontalScrollOffset;
5295 	return rect;
5296 }
5297 /**
5298  * Returns the widget content starting at start for length characters.
5299  *
5300  * @param start offset of the first character in the returned String
5301  * @param length number of characters to return
5302  * @return widget content starting at start and extending length characters.
5303  * @exception SWTException <ul>
5304  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5305  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5306  * </ul>
5307  * @exception IllegalArgumentException <ul>
5308  *   <li>ERROR_INVALID_RANGE when start and/or length are outside the widget content</li>
5309  * </ul>
5310  */
getTextRange(int start, int length)5311 public String getTextRange(int start, int length) {
5312 	checkWidget();
5313 	int contentLength = getCharCount();
5314 	int end = start + length;
5315 	if (start > end || start < 0 || end > contentLength) {
5316 		SWT.error(SWT.ERROR_INVALID_RANGE);
5317 	}
5318 	return content.getTextRange(start, length);
5319 }
5320 /**
5321  * Returns the maximum number of characters that the receiver is capable of holding.
5322  *
5323  * @return the text limit
5324  *
5325  * @exception SWTException <ul>
5326  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5327  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5328  * </ul>
5329  */
getTextLimit()5330 public int getTextLimit() {
5331 	checkWidget();
5332 	return textLimit;
5333 }
5334 /**
5335  * Gets the top index.
5336  * <p>
5337  * The top index is the index of the fully visible line that is currently
5338  * at the top of the widget or the topmost partially visible line if no line is fully visible.
5339  * The top index changes when the widget is scrolled. Indexing is zero based.
5340  * </p>
5341  *
5342  * @return the index of the top line
5343  * @exception SWTException <ul>
5344  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5345  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5346  * </ul>
5347  */
getTopIndex()5348 public int getTopIndex() {
5349 	checkWidget();
5350 	return topIndex;
5351 }
5352 /**
5353  * Returns the top margin.
5354  *
5355  * @return the top margin.
5356  * @exception SWTException <ul>
5357  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5358  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5359  * </ul>
5360  *
5361  * @since 3.5
5362  */
getTopMargin()5363 public int getTopMargin() {
5364 	checkWidget();
5365 	return topMargin;
5366 }
5367 /**
5368  * Gets the top SWT logical point.
5369  * <p>
5370  * The top point is the SWT logical point position of the line that is
5371  * currently at the top of the widget. The text widget can be scrolled by points
5372  * by dragging the scroll thumb so that a partial line may be displayed at the top
5373  * the widget.  The top point changes when the widget is scrolled.  The top point
5374  * does not include the widget trimming.
5375  * </p>
5376  *
5377  * @return SWT logical point position of the top line
5378  * @exception SWTException <ul>
5379  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5380  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5381  * </ul>
5382  */
getTopPixel()5383 public int getTopPixel() {
5384 	checkWidget();
5385 	return getVerticalScrollOffset();
5386 }
5387 /**
5388  * Returns the vertical scroll increment.
5389  *
5390  * @return vertical scroll increment.
5391  */
getVerticalIncrement()5392 int getVerticalIncrement() {
5393 	return renderer.getLineHeight();
5394 }
getVerticalScrollOffset()5395 int getVerticalScrollOffset() {
5396 	if (verticalScrollOffset == -1) {
5397 		renderer.calculate(0, topIndex);
5398 		int height = 0;
5399 		for (int i = 0; i < topIndex; i++) {
5400 			height += renderer.getCachedLineHeight(i);
5401 		}
5402 		height -= topIndexY;
5403 		verticalScrollOffset = height;
5404 	}
5405 	return verticalScrollOffset;
5406 }
getVisualLineIndex(TextLayout layout, int offsetInLine)5407 int getVisualLineIndex(TextLayout layout, int offsetInLine) {
5408 	int lineIndex = layout.getLineIndex(offsetInLine);
5409 	int[] offsets = layout.getLineOffsets();
5410 	Caret caret = getCaret();
5411 	if (caret != null && lineIndex != 0 && offsetInLine == offsets[lineIndex]) {
5412 		int lineY = layout.getLineBounds(lineIndex).y;
5413 		int caretY = caret.getLocation().y - getLinePixel(getCaretLine());
5414 		if (lineY > caretY) lineIndex--;
5415 		caretAlignment = OFFSET_LEADING;
5416 	}
5417 	return lineIndex;
5418 }
getCaretDirection()5419 int getCaretDirection() {
5420 	if (!isBidiCaret()) return SWT.DEFAULT;
5421 	if (ime.getCompositionOffset() != -1) return SWT.DEFAULT;
5422 	if (!updateCaretDirection && caretDirection != SWT.NULL) return caretDirection;
5423 	updateCaretDirection = false;
5424 	int caretLine = getCaretLine();
5425 	int lineOffset = content.getOffsetAtLine(caretLine);
5426 	String line = content.getLine(caretLine);
5427 	int offset = caretOffset - lineOffset;
5428 	int lineLength = line.length();
5429 	if (lineLength == 0) return isMirrored() ? SWT.RIGHT : SWT.LEFT;
5430 	if (caretAlignment == PREVIOUS_OFFSET_TRAILING && offset > 0) offset--;
5431 	if (offset == lineLength && offset > 0) offset--;
5432 	while (offset > 0 && Character.isDigit(line.charAt(offset))) offset--;
5433 	if (offset == 0 && Character.isDigit(line.charAt(offset))) {
5434 		return isMirrored() ? SWT.RIGHT : SWT.LEFT;
5435 	}
5436 	TextLayout layout = renderer.getTextLayout(caretLine);
5437 	int level = layout.getLevel(offset);
5438 	renderer.disposeTextLayout(layout);
5439 	return ((level & 1) != 0) ? SWT.RIGHT : SWT.LEFT;
5440 }
5441 /*
5442  * Returns the index of the line the caret is on.
5443  */
getCaretLine()5444 int getCaretLine() {
5445 	return content.getLineAtOffset(caretOffset);
5446 }
getWrapWidth()5447 int getWrapWidth () {
5448 	if (wordWrap && !isSingleLine()) {
5449 		int width = clientAreaWidth - leftMargin - rightMargin;
5450 		return width > 0 ? width : 1;
5451 	}
5452 	return -1;
5453 }
getWordNext(int offset, int movement)5454 int getWordNext (int offset, int movement) {
5455 	return getWordNext(offset, movement, false);
5456 }
getWordNext(int offset, int movement, boolean ignoreListener)5457 int getWordNext (int offset, int movement, boolean ignoreListener) {
5458 	int newOffset, lineOffset;
5459 	String lineText;
5460 	if (offset >= getCharCount()) {
5461 		newOffset = offset;
5462 		int lineIndex = content.getLineCount() - 1;
5463 		lineOffset = content.getOffsetAtLine(lineIndex);
5464 		lineText = content.getLine(lineIndex);
5465 	} else {
5466 		int lineIndex = content.getLineAtOffset(offset);
5467 		lineOffset = content.getOffsetAtLine(lineIndex);
5468 		lineText = content.getLine(lineIndex);
5469 		int lineLength = lineText.length();
5470 		if (offset >= lineOffset + lineLength) {
5471 			newOffset = content.getOffsetAtLine(lineIndex + 1);
5472 		} else {
5473 			TextLayout layout = renderer.getTextLayout(lineIndex);
5474 			newOffset = lineOffset + layout.getNextOffset(offset - lineOffset, movement);
5475 			renderer.disposeTextLayout(layout);
5476 		}
5477 	}
5478 	if (ignoreListener) return newOffset;
5479 	return sendWordBoundaryEvent(ST.WordNext, movement, offset, newOffset, lineText, lineOffset);
5480 }
getWordPrevious(int offset, int movement)5481 int getWordPrevious(int offset, int movement) {
5482 	return getWordPrevious(offset, movement, false);
5483 }
getWordPrevious(int offset, int movement, boolean ignoreListener)5484 int getWordPrevious(int offset, int movement, boolean ignoreListener) {
5485 	int newOffset, lineOffset;
5486 	String lineText;
5487 	if (offset <= 0) {
5488 		newOffset = 0;
5489 		int lineIndex = content.getLineAtOffset(newOffset);
5490 		lineOffset = content.getOffsetAtLine(lineIndex);
5491 		lineText = content.getLine(lineIndex);
5492 	} else {
5493 		int lineIndex = content.getLineAtOffset(offset);
5494 		lineOffset = content.getOffsetAtLine(lineIndex);
5495 		lineText = content.getLine(lineIndex);
5496 		if (offset == lineOffset) {
5497 			String nextLineText = content.getLine(lineIndex - 1);
5498 			int nextLineOffset = content.getOffsetAtLine(lineIndex - 1);
5499 			newOffset = nextLineOffset + nextLineText.length();
5500 		} else {
5501 			int layoutOffset = Math.min(offset - lineOffset, lineText.length());
5502 			TextLayout layout = renderer.getTextLayout(lineIndex);
5503 			newOffset = lineOffset + layout.getPreviousOffset(layoutOffset, movement);
5504 			renderer.disposeTextLayout(layout);
5505 		}
5506 	}
5507 	if (ignoreListener) return newOffset;
5508 	return sendWordBoundaryEvent(ST.WordPrevious, movement, offset, newOffset, lineText, lineOffset);
5509 }
5510 /**
5511  * Returns whether the widget wraps lines.
5512  *
5513  * @return true if widget wraps lines, false otherwise
5514  * @since 2.0
5515  */
getWordWrap()5516 public boolean getWordWrap() {
5517 	checkWidget();
5518 	return wordWrap;
5519 }
5520 /**
5521  * Returns the wrap indentation of the widget.
5522  *
5523  * @return the wrap indentation
5524  *
5525  * @exception SWTException <ul>
5526  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5527  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5528  * </ul>
5529  *
5530  * @see #getLineWrapIndent(int)
5531  *
5532  * @since 3.6
5533  */
getWrapIndent()5534 public int getWrapIndent() {
5535 	checkWidget();
5536 	return wrapIndent;
5537 }
5538 /**
5539  * Returns the location of the given offset.
5540  * <p>
5541  * <b>NOTE:</b> Does not return correct values for true italic fonts (vs. slanted fonts).
5542  * </p>
5543  *
5544  * @return location of the character at the given offset in the line.
5545  */
getPointAtOffset(int offset)5546 Point getPointAtOffset(int offset) {
5547 	int lineIndex = content.getLineAtOffset(offset);
5548 	String line = content.getLine(lineIndex);
5549 	int lineOffset = content.getOffsetAtLine(lineIndex);
5550 	int offsetInLine = Math.max (0, offset - lineOffset);
5551 	int lineLength = line.length();
5552 	if (lineIndex < content.getLineCount() - 1) {
5553 		int endLineOffset = content.getOffsetAtLine(lineIndex + 1) - 1;
5554 		if (lineLength < offsetInLine && offsetInLine <= endLineOffset) {
5555 			offsetInLine = lineLength;
5556 		}
5557 	}
5558 	Point point;
5559 	TextLayout layout = renderer.getTextLayout(lineIndex);
5560 	if (lineLength != 0  && offsetInLine <= lineLength) {
5561 		if (offsetInLine == lineLength) {
5562 			offsetInLine = layout.getPreviousOffset(offsetInLine, SWT.MOVEMENT_CLUSTER);
5563 			point = layout.getLocation(offsetInLine, true);
5564 		} else {
5565 			switch (caretAlignment) {
5566 				case OFFSET_LEADING:
5567 					point = layout.getLocation(offsetInLine, false);
5568 					break;
5569 				case PREVIOUS_OFFSET_TRAILING:
5570 				default:
5571 					boolean lineBegin = offsetInLine == 0;
5572 					// If word wrap is enabled, we should also consider offsets
5573 					// of wrapped line parts as line begin and do NOT go back.
5574 					// This prevents clients to jump one line higher than
5575 					// expected, see bug 488172.
5576 					// Respect caretAlignment at the caretOffset, unless there's
5577 					// a non-empty selection, see bug 488172 comment 6.
5578 					if (wordWrap && !lineBegin && (offset != caretOffset || selection.x != selection.y)) {
5579 						int[] offsets = layout.getLineOffsets();
5580 						for (int i : offsets) {
5581 							if (i == offsetInLine) {
5582 								lineBegin = true;
5583 								break;
5584 							}
5585 						}
5586 					}
5587 					if (lineBegin) {
5588 						point = layout.getLocation(offsetInLine, false);
5589 					} else {
5590 						offsetInLine = layout.getPreviousOffset(offsetInLine, SWT.MOVEMENT_CLUSTER);
5591 						point = layout.getLocation(offsetInLine, true);
5592 					}
5593 					break;
5594 			}
5595 		}
5596 	} else {
5597 		point = new Point(layout.getIndent(), layout.getVerticalIndent());
5598 	}
5599 	renderer.disposeTextLayout(layout);
5600 	point.x += leftMargin - horizontalScrollOffset;
5601 	point.y += getLinePixel(lineIndex);
5602 	return point;
5603 }
5604 /**
5605  * Inserts a string.  The old selection is replaced with the new text.
5606  *
5607  * @param string the string
5608  * @see #replaceTextRange(int,int,String)
5609  * @exception SWTException <ul>
5610  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5611  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5612  * </ul>
5613  * @exception IllegalArgumentException <ul>
5614  *    <li>ERROR_NULL_ARGUMENT when string is null</li>
5615  * </ul>
5616  */
insert(String string)5617 public void insert(String string) {
5618 	checkWidget();
5619 	if (string == null) {
5620 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
5621 	}
5622 	if (blockSelection) {
5623 		insertBlockSelectionText(string, false);
5624 	} else {
5625 		Point sel = getSelectionRange();
5626 		replaceTextRange(sel.x, sel.y, string);
5627 	}
5628 }
insertBlockSelectionText(String text, boolean fillWithSpaces)5629 int insertBlockSelectionText(String text, boolean fillWithSpaces) {
5630 	int lineCount = 1;
5631 	for (int i = 0; i < text.length(); i++) {
5632 		char ch = text.charAt(i);
5633 		if (ch == '\n' || ch == '\r') {
5634 			lineCount++;
5635 			if (ch == '\r' && i + 1 < text.length() && text.charAt(i + 1) == '\n') {
5636 				i++;
5637 			}
5638 		}
5639 	}
5640 	String[] lines = new String[lineCount];
5641 	int start = 0;
5642 	lineCount = 0;
5643 	for (int i = 0; i < text.length(); i++) {
5644 		char ch = text.charAt(i);
5645 		if (ch == '\n' || ch == '\r') {
5646 			lines[lineCount++] = text.substring(start, i);
5647 			if (ch == '\r' && i + 1 < text.length() && text.charAt(i + 1) == '\n') {
5648 				i++;
5649 			}
5650 			start = i + 1;
5651 		}
5652 	}
5653 	lines[lineCount++] = text.substring(start);
5654 	if (fillWithSpaces) {
5655 		int maxLength = 0;
5656 		for (String line : lines) {
5657 			int length = line.length();
5658 			maxLength = Math.max(maxLength, length);
5659 		}
5660 		for (int i = 0; i < lines.length; i++) {
5661 			String line = lines[i];
5662 			int length = line.length();
5663 			if (length < maxLength) {
5664 				int numSpaces = maxLength - length;
5665 				StringBuilder buffer = new StringBuilder(length + numSpaces);
5666 				buffer.append(line);
5667 				for (int j = 0; j < numSpaces; j++) buffer.append(' ');
5668 				lines[i] = buffer.toString();
5669 			}
5670 		}
5671 	}
5672 	int firstLine, lastLine, left, right;
5673 	if (blockXLocation != -1) {
5674 		Rectangle rect = getBlockSelectionPosition();
5675 		firstLine = rect.y;
5676 		lastLine = rect.height;
5677 		left = rect.x;
5678 		right = rect.width;
5679 	} else {
5680 		firstLine = lastLine = getCaretLine();
5681 		left = right = getPointAtOffset(caretOffset).x;
5682 	}
5683 	start = caretOffset;
5684 	int caretLine = getCaretLine();
5685 	int index = 0, lineIndex = firstLine;
5686 	while (lineIndex <= lastLine) {
5687 		String string = index < lineCount ? lines[index++] : "";
5688 		int lineStart = sendTextEvent(left, right, lineIndex, string, fillWithSpaces);
5689 		if (lineIndex == caretLine) start = lineStart;
5690 		lineIndex++;
5691 	}
5692 	while (index < lineCount) {
5693 		int lineStart = sendTextEvent(left, left, lineIndex, lines[index++], fillWithSpaces);
5694 		if (lineIndex == caretLine) start = lineStart;
5695 		lineIndex++;
5696 	}
5697 	return start;
5698 }
5699 void insertBlockSelectionText(char key, int action) {
5700 	if (key == SWT.CR || key == SWT.LF) return;
5701 	Rectangle rect = getBlockSelectionPosition();
5702 	int firstLine = rect.y;
5703 	int lastLine = rect.height;
5704 	int left = rect.x;
5705 	int right = rect.width;
5706 	int[] trailing = new int[1];
5707 	int offset = 0, delta = 0;
5708 	String text = key != 0 ? new String(new char[] {key}) : "";
5709 	int length = text.length();
5710 	for (int lineIndex = firstLine; lineIndex <= lastLine; lineIndex++) {
5711 		String line = content.getLine(lineIndex);
5712 		int lineOffset = content.getOffsetAtLine(lineIndex);
5713 		int lineEndOffset = lineOffset + line.length();
5714 		int linePixel = getLinePixel(lineIndex);
5715 		int start = getOffsetAtPoint(left, linePixel, trailing, true);
5716 		boolean outOfLine = start == -1;
5717 		if (outOfLine) {
5718 			start = left < leftMargin ? lineOffset : lineEndOffset;
5719 		} else {
5720 			start += trailing[0];
5721 		}
5722 		int end = getOffsetAtPoint(right, linePixel, trailing, true);
5723 		if (end == -1) {
5724 			end = right < leftMargin ? lineOffset : lineEndOffset;
5725 		} else {
5726 			end += trailing[0];
5727 		}
5728 		if (start > end) {
5729 			int temp = start;
5730 			start = end;
5731 			end = temp;
5732 		}
5733 		if (start == end && !outOfLine) {
5734 			switch (action) {
5735 				case ST.DELETE_PREVIOUS:
5736 					if (start > lineOffset) start = getClusterPrevious(start, lineIndex);
5737 					break;
5738 				case ST.DELETE_NEXT:
5739 					if (end < lineEndOffset) end = getClusterNext(end, lineIndex);
5740 					break;
5741 			}
5742 		}
5743 		if (outOfLine) {
5744 			if (line.length() >= delta) {
5745 				delta = line.length();
5746 				offset = lineEndOffset + length;
5747 			}
5748 		} else {
5749 			offset = start + length;
5750 			delta = content.getCharCount();
5751 		}
5752 		Event event = new Event();
5753 		event.text = text;
5754 		event.start = start;
5755 		event.end = end;
5756 		sendKeyEvent(event);
5757 	}
5758 	int x = getPointAtOffset(offset).x;
5759 	int verticalScrollOffset = getVerticalScrollOffset();
5760 	setBlockSelectionLocation(x, blockYAnchor - verticalScrollOffset, x, blockYLocation - verticalScrollOffset, false);
5761 }
5762 /**
5763  * Creates content change listeners and set the default content model.
5764  */
5765 void installDefaultContent() {
5766 	textChangeListener = new TextChangeListener() {
5767 		@Override
5768 		public void textChanging(TextChangingEvent event) {
5769 			handleTextChanging(event);
5770 		}
5771 		@Override
5772 		public void textChanged(TextChangedEvent event) {
5773 			handleTextChanged(event);
5774 		}
5775 		@Override
5776 		public void textSet(TextChangedEvent event) {
5777 			handleTextSet(event);
5778 		}
5779 	};
5780 	content = new DefaultContent();
5781 	content.addTextChangeListener(textChangeListener);
5782 }
5783 /**
5784  * Adds event listeners
5785  */
5786 void installListeners() {
5787 	ScrollBar verticalBar = getVerticalBar();
5788 	ScrollBar horizontalBar = getHorizontalBar();
5789 
5790 	listener = event -> {
5791 		switch (event.type) {
5792 			case SWT.Dispose: handleDispose(event); break;
5793 			case SWT.KeyDown: handleKeyDown(event); break;
5794 			case SWT.KeyUp: handleKeyUp(event); break;
5795 			case SWT.MenuDetect: handleMenuDetect(event); break;
5796 			case SWT.MouseDown: handleMouseDown(event); break;
5797 			case SWT.MouseUp: handleMouseUp(event); break;
5798 			case SWT.MouseMove: handleMouseMove(event); break;
5799 			case SWT.Paint: handlePaint(event); break;
5800 			case SWT.Resize: handleResize(event); break;
5801 			case SWT.Traverse: handleTraverse(event); break;
5802 		}
5803 	};
5804 	addListener(SWT.Dispose, listener);
5805 	addListener(SWT.KeyDown, listener);
5806 	addListener(SWT.KeyUp, listener);
5807 	addListener(SWT.MenuDetect, listener);
5808 	addListener(SWT.MouseDown, listener);
5809 	addListener(SWT.MouseUp, listener);
5810 	addListener(SWT.MouseMove, listener);
5811 	addListener(SWT.Paint, listener);
5812 	addListener(SWT.Resize, listener);
5813 	addListener(SWT.Traverse, listener);
5814 	ime.addListener(SWT.ImeComposition, event -> {
5815 		if (!editable) {
5816 			event.doit = false;
5817 			event.start = 0;
5818 			event.end = 0;
5819 			event.text = "";
5820 			return;
5821 		}
5822 
5823 		switch (event.detail) {
5824 			case SWT.COMPOSITION_SELECTION: handleCompositionSelection(event); break;
5825 			case SWT.COMPOSITION_CHANGED: handleCompositionChanged(event); break;
5826 			case SWT.COMPOSITION_OFFSET: handleCompositionOffset(event); break;
5827 		}
5828 	});
5829 	if (verticalBar != null) {
5830 		verticalBar.addListener(SWT.Selection, event -> handleVerticalScroll(event));
5831 	}
5832 	if (horizontalBar != null) {
5833 		horizontalBar.addListener(SWT.Selection, event -> handleHorizontalScroll(event));
5834 	}
5835 }
internalRedrawRange(int start, int length)5836 void internalRedrawRange(int start, int length) {
5837 	if (length <= 0) return;
5838 	int end = start + length;
5839 	int startLine = content.getLineAtOffset(start);
5840 	int endLine = content.getLineAtOffset(end);
5841 	int partialBottomIndex = getPartialBottomIndex();
5842 	int partialTopIndex = getPartialTopIndex();
5843 	if (startLine > partialBottomIndex || endLine < partialTopIndex) {
5844 		return;
5845 	}
5846 	if (partialTopIndex > startLine) {
5847 		startLine = partialTopIndex;
5848 		start = 0;
5849 	} else {
5850 		start -= content.getOffsetAtLine(startLine);
5851 	}
5852 	if (partialBottomIndex < endLine) {
5853 		endLine = partialBottomIndex + 1;
5854 		end = 0;
5855 	} else {
5856 		end -= content.getOffsetAtLine(endLine);
5857 	}
5858 
5859 	TextLayout layout = renderer.getTextLayout(startLine);
5860 	int lineX = leftMargin - horizontalScrollOffset, startLineY = getLinePixel(startLine);
5861 	int[] offsets = layout.getLineOffsets();
5862 	int startIndex = layout.getLineIndex(Math.min(start, layout.getText().length()));
5863 
5864 	/* Redraw end of line before start line if wrapped and start offset is first char */
5865 	if (isWordWrap() && startIndex > 0 && offsets[startIndex] == start) {
5866 		Rectangle rect = layout.getLineBounds(startIndex - 1);
5867 		rect.x = rect.width;
5868 		rect.width = clientAreaWidth - rightMargin - rect.x;
5869 		rect.x += lineX;
5870 		rect.y += startLineY;
5871 		super.redraw(rect.x, rect.y, rect.width, rect.height, false);
5872 	}
5873 
5874 	if (startLine == endLine) {
5875 		int endIndex = layout.getLineIndex(Math.min(end, layout.getText().length()));
5876 		if (startIndex == endIndex) {
5877 			/* Redraw rect between start and end offset if start and end offsets are in same wrapped line */
5878 			Rectangle rect = layout.getBounds(start, end - 1);
5879 			rect.x += lineX;
5880 			rect.y += startLineY;
5881 			super.redraw(rect.x, rect.y, rect.width, rect.height, false);
5882 			renderer.disposeTextLayout(layout);
5883 			return;
5884 		}
5885 	}
5886 
5887 	/* Redraw start line from the start offset to the end of client area */
5888 	Rectangle startRect = layout.getBounds(start, offsets[startIndex + 1] - 1);
5889 	if (startRect.height == 0) {
5890 		Rectangle bounds = layout.getLineBounds(startIndex);
5891 		startRect.x = bounds.width;
5892 		startRect.y = bounds.y;
5893 		startRect.height = bounds.height;
5894 	}
5895 	startRect.x += lineX;
5896 	startRect.y += startLineY;
5897 	startRect.width = clientAreaWidth - rightMargin - startRect.x;
5898 	super.redraw(startRect.x, startRect.y, startRect.width, startRect.height, false);
5899 
5900 	/* Redraw end line from the beginning of the line to the end offset */
5901 	if (startLine != endLine) {
5902 		renderer.disposeTextLayout(layout);
5903 		layout = renderer.getTextLayout(endLine);
5904 		offsets = layout.getLineOffsets();
5905 	}
5906 	int endIndex = layout.getLineIndex(Math.min(end, layout.getText().length()));
5907 	Rectangle endRect = layout.getBounds(offsets[endIndex], end - 1);
5908 	if (endRect.height == 0) {
5909 		Rectangle bounds = layout.getLineBounds(endIndex);
5910 		endRect.y = bounds.y;
5911 		endRect.height = bounds.height;
5912 	}
5913 	endRect.x += lineX;
5914 	endRect.y += getLinePixel(endLine);
5915 	super.redraw(endRect.x, endRect.y, endRect.width, endRect.height, false);
5916 	renderer.disposeTextLayout(layout);
5917 
5918 	/* Redraw all lines in between start and end line */
5919 	int y = startRect.y + startRect.height;
5920 	if (endRect.y > y) {
5921 		super.redraw(leftMargin, y, clientAreaWidth - rightMargin - leftMargin, endRect.y - y, false);
5922 	}
5923 }
handleCompositionOffset(Event event)5924 void handleCompositionOffset (Event event) {
5925 	int[] trailing = new int [1];
5926 	event.index = getOffsetAtPoint(event.x, event.y, trailing, true);
5927 	event.count = trailing[0];
5928 }
handleCompositionSelection(Event event)5929 void handleCompositionSelection (Event event) {
5930 	if (event.start != event.end) {
5931 		int charCount = getCharCount();
5932 		event.start = Math.max(0, Math.min(event.start, charCount));
5933 		event.end = Math.max(0, Math.min(event.end, charCount));
5934 		if (event.text != null) {
5935 			setSelection(event.start, event.end);
5936 		} else {
5937 			event.text = getTextRange(event.start, event.end - event.start);
5938 		}
5939 	} else {
5940 		event.start = selection.x;
5941 		event.end = selection.y;
5942 		event.text = getSelectionText();
5943 	}
5944 }
handleCompositionChanged(Event event)5945 void handleCompositionChanged(Event event) {
5946 	String text = event.text;
5947 	int start = event.start;
5948 	int end = event.end;
5949 	int charCount = content.getCharCount();
5950 	start = Math.min(start, charCount);
5951 	end = Math.min(end, charCount);
5952 	int length = text.length();
5953 	if (length == ime.getCommitCount()) {
5954 		content.replaceTextRange(start, end - start, "");
5955 		setCaretOffset(ime.getCompositionOffset(), SWT.DEFAULT);
5956 		caretWidth = 0;
5957 		caretDirection = SWT.NULL;
5958 	} else {
5959 		content.replaceTextRange(start, end - start, text);
5960 		int alignment = SWT.DEFAULT;
5961 		if (ime.getWideCaret()) {
5962 			start = ime.getCompositionOffset();
5963 			int lineIndex = getCaretLine();
5964 			int lineOffset = content.getOffsetAtLine(lineIndex);
5965 			TextLayout layout = renderer.getTextLayout(lineIndex);
5966 			caretWidth = layout.getBounds(start - lineOffset, start + length - 1 - lineOffset).width;
5967 			renderer.disposeTextLayout(layout);
5968 			alignment = OFFSET_LEADING;
5969 		}
5970 		setCaretOffset(ime.getCaretOffset(), alignment);
5971 	}
5972 	resetSelection();
5973 	showCaret();
5974 }
5975 /**
5976  * Frees resources.
5977  */
handleDispose(Event event)5978 void handleDispose(Event event) {
5979 	removeListener(SWT.Dispose, listener);
5980 	notifyListeners(SWT.Dispose, event);
5981 	event.type = SWT.None;
5982 
5983 	clipboard.dispose();
5984 	if (renderer != null) {
5985 		renderer.dispose();
5986 		renderer = null;
5987 	}
5988 	if (content != null) {
5989 		content.removeTextChangeListener(textChangeListener);
5990 		content = null;
5991 	}
5992 	if (defaultCaret != null) {
5993 		defaultCaret.dispose();
5994 		defaultCaret = null;
5995 	}
5996 	if (leftCaretBitmap != null) {
5997 		leftCaretBitmap.dispose();
5998 		leftCaretBitmap = null;
5999 	}
6000 	if (rightCaretBitmap != null) {
6001 		rightCaretBitmap.dispose();
6002 		rightCaretBitmap = null;
6003 	}
6004 	if (isBidiCaret()) {
6005 		BidiUtil.removeLanguageListener(this);
6006 	}
6007 	selectionBackground = null;
6008 	selectionForeground = null;
6009 	marginColor = null;
6010 	textChangeListener = null;
6011 	selection = null;
6012 	doubleClickSelection = null;
6013 	keyActionMap = null;
6014 	background = null;
6015 	foreground = null;
6016 	clipboard = null;
6017 	tabs = null;
6018 }
6019 /**
6020  * Scrolls the widget horizontally.
6021  */
handleHorizontalScroll(Event event)6022 void handleHorizontalScroll(Event event) {
6023 	int scrollPixel = getHorizontalBar().getSelection() - horizontalScrollOffset;
6024 	scrollHorizontal(scrollPixel, false);
6025 }
6026 /**
6027  * If an action has been registered for the key stroke execute the action.
6028  * Otherwise, if a character has been entered treat it as new content.
6029  *
6030  * @param event keyboard event
6031  */
handleKey(Event event)6032 void handleKey(Event event) {
6033 	int action;
6034 	caretAlignment = PREVIOUS_OFFSET_TRAILING;
6035 	if (event.keyCode != 0) {
6036 		// special key pressed (e.g., F1)
6037 		action = getKeyBinding(event.keyCode | event.stateMask);
6038 	} else {
6039 		// character key pressed
6040 		action = getKeyBinding(event.character | event.stateMask);
6041 		if (action == SWT.NULL) {
6042 			// see if we have a control character
6043 			if ((event.stateMask & SWT.CTRL) != 0 && event.character <= 31) {
6044 				// get the character from the CTRL+char sequence, the control
6045 				// key subtracts 64 from the value of the key that it modifies
6046 				int c = event.character + 64;
6047 				action = getKeyBinding(c | event.stateMask);
6048 			}
6049 		}
6050 	}
6051 	if (action == SWT.NULL) {
6052 		boolean ignore = false;
6053 
6054 		if (IS_MAC) {
6055 			// Ignore accelerator key combinations (we do not want to
6056 			// insert a character in the text in this instance).
6057 			ignore = (event.stateMask & (SWT.COMMAND | SWT.CTRL)) != 0;
6058 		} else {
6059 			// Ignore accelerator key combinations (we do not want to
6060 			// insert a character in the text in this instance). Don't
6061 			// ignore CTRL+ALT combinations since that is the Alt Gr
6062 			// key on some keyboards.  See bug 20953.
6063 			ignore = event.stateMask == SWT.ALT ||
6064 					event.stateMask == SWT.CTRL ||
6065 					event.stateMask == (SWT.ALT | SWT.SHIFT) ||
6066 					event.stateMask == (SWT.CTRL | SWT.SHIFT);
6067 		}
6068 		// -ignore anything below SPACE except for line delimiter keys and tab.
6069 		// -ignore DEL
6070 		if (!ignore && event.character > 31 && event.character != SWT.DEL ||
6071 			event.character == SWT.CR || event.character == SWT.LF ||
6072 			event.character == TAB) {
6073 			doContent(event.character);
6074 			update();
6075 		}
6076 	} else {
6077 		invokeAction(action);
6078 	}
6079 }
6080 /**
6081  * If a VerifyKey listener exists, verify that the key that was entered
6082  * should be processed.
6083  *
6084  * @param event keyboard event
6085  */
handleKeyDown(Event event)6086 void handleKeyDown(Event event) {
6087 	if (clipboardSelection == null) {
6088 		clipboardSelection = new Point(selection.x, selection.y);
6089 	}
6090 	newOrientation = SWT.NONE;
6091 	event.stateMask &= SWT.MODIFIER_MASK;
6092 
6093 	Event verifyEvent = new Event();
6094 	verifyEvent.character = event.character;
6095 	verifyEvent.keyCode = event.keyCode;
6096 	verifyEvent.keyLocation = event.keyLocation;
6097 	verifyEvent.stateMask = event.stateMask;
6098 	verifyEvent.doit = event.doit;
6099 	notifyListeners(ST.VerifyKey, verifyEvent);
6100 	if (verifyEvent.doit) {
6101 		if ((event.stateMask & SWT.MODIFIER_MASK) == SWT.CTRL && event.keyCode == SWT.SHIFT && isBidiCaret()) {
6102 			newOrientation = event.keyLocation == SWT.LEFT ? SWT.LEFT_TO_RIGHT : SWT.RIGHT_TO_LEFT;
6103 		}
6104 		handleKey(event);
6105 	}
6106 }
6107 /**
6108  * Update the Selection Clipboard.
6109  *
6110  * @param event keyboard event
6111  */
handleKeyUp(Event event)6112 void handleKeyUp(Event event) {
6113 	if (clipboardSelection != null) {
6114 		if (clipboardSelection.x != selection.x || clipboardSelection.y != selection.y) {
6115 			copySelection(DND.SELECTION_CLIPBOARD);
6116 		}
6117 	}
6118 	clipboardSelection = null;
6119 
6120 	if (newOrientation != SWT.NONE) {
6121 		if (newOrientation != getOrientation()) {
6122 			Event e = new Event();
6123 			e.doit = true;
6124 			notifyListeners(SWT.OrientationChange, e);
6125 			if (e.doit) {
6126 				setOrientation(newOrientation);
6127 			}
6128 		}
6129 		newOrientation = SWT.NONE;
6130 	}
6131 }
6132 /**
6133  * Update the event location for focus-based context menu triggers.
6134  *
6135  * @param event menu detect event
6136  */
handleMenuDetect(Event event)6137 void handleMenuDetect(Event event) {
6138 	if (event.detail == SWT.MENU_KEYBOARD) {
6139 		Point point = getDisplay().map(this, null, getPointAtOffset(caretOffset));
6140 		event.x = point.x;
6141 		event.y = point.y + getLineHeight(caretOffset);
6142 	}
6143 }
6144 /**
6145  * Updates the caret location and selection if mouse button 1 has been
6146  * pressed.
6147  */
handleMouseDown(Event event)6148 void handleMouseDown(Event event) {
6149 	//force focus (object support)
6150 	forceFocus();
6151 
6152 	//drag detect
6153 	if (dragDetect && checkDragDetect(event)) return;
6154 
6155 	//paste clipboard selection
6156 	if (event.button == 2) {
6157 		// On GTK, if mouseNavigator is enabled we have to distinguish a short middle-click (to paste content) from
6158 		// a long middle-click (mouse navigation started)
6159 		if (IS_GTK && mouseNavigator != null) {
6160 			middleClickPressed = true;
6161 			getDisplay().timerExec(200, ()->{
6162 				boolean click = middleClickPressed;
6163 				middleClickPressed = false;
6164 				if (click && mouseNavigator !=null) {
6165 					mouseNavigator.onMouseDown(event);
6166 				} else {
6167 					pasteOnMiddleClick(event);
6168 				}
6169 			});
6170 			return;
6171 		} else {
6172 			pasteOnMiddleClick(event);
6173 		}
6174 	}
6175 
6176 	//set selection
6177 	if ((event.button != 1) || (IS_MAC && (event.stateMask & SWT.MOD4) != 0)) {
6178 		return;
6179 	}
6180 	clickCount = event.count;
6181 	if (clickCount == 1) {
6182 		boolean select = (event.stateMask & SWT.MOD2) != 0;
6183 		doMouseLocationChange(event.x, event.y, select);
6184 	} else {
6185 		if (doubleClickEnabled) {
6186 			boolean wordSelect = (clickCount & 1) == 0;
6187 			int offset = getOffsetAtPoint(event.x, event.y, null);
6188 			int lineIndex = content.getLineAtOffset(offset);
6189 			int lineOffset = content.getOffsetAtLine(lineIndex);
6190 			if (wordSelect) {
6191 				int min = blockSelection ? lineOffset : 0;
6192 				int max = blockSelection ? lineOffset + content.getLine(lineIndex).length() : content.getCharCount();
6193 				int start = Math.max(min, getWordPrevious(offset, SWT.MOVEMENT_WORD_START));
6194 				int end = Math.min(max, getWordNext(start, SWT.MOVEMENT_WORD_END));
6195 				setSelection(start, end - start, false, true);
6196 				sendSelectionEvent();
6197 			} else {
6198 				if (blockSelection) {
6199 					setBlockSelectionLocation(leftMargin, event.y, clientAreaWidth - rightMargin, event.y, true);
6200 				} else {
6201 					int lineEnd = content.getCharCount();
6202 					if (lineIndex + 1 < content.getLineCount()) {
6203 						lineEnd = content.getOffsetAtLine(lineIndex + 1);
6204 					}
6205 					setSelection(lineOffset, lineEnd - lineOffset, false, false);
6206 					sendSelectionEvent();
6207 				}
6208 			}
6209 			doubleClickSelection = new Point(selection.x, selection.y);
6210 			showCaret();
6211 		}
6212 	}
6213 }
6214 /**
6215  * Updates the caret location and selection if mouse button 1 is pressed
6216  * during the mouse move.
6217  */
handleMouseMove(Event event)6218 void handleMouseMove(Event event) {
6219 	if (clickCount > 0) {
6220 		update();
6221 		doAutoScroll(event);
6222 		doMouseLocationChange(event.x, event.y, true);
6223 	}
6224 	if (renderer.hasLinks) {
6225 		doMouseLinkCursor(event.x, event.y);
6226 	}
6227 }
6228 /**
6229  * Autoscrolling ends when the mouse button is released.
6230  */
handleMouseUp(Event event)6231 void handleMouseUp(Event event) {
6232 	middleClickPressed = false;
6233 	clickCount = 0;
6234 	endAutoScroll();
6235 	if (event.button == 1) {
6236 		copySelection(DND.SELECTION_CLIPBOARD);
6237 	}
6238 }
6239 /**
6240  * Renders the invalidated area specified in the paint event.
6241  *
6242  * @param event paint event
6243  */
handlePaint(Event event)6244 void handlePaint(Event event) {
6245 	if (event.width == 0 || event.height == 0) return;
6246 	if (clientAreaWidth == 0 || clientAreaHeight == 0) return;
6247 
6248 	int startLine = getLineIndex(event.y);
6249 	int y = getLinePixel(startLine);
6250 	int endY = event.y + event.height;
6251 	GC gc = event.gc;
6252 	Color background = getBackground();
6253 	Color foreground = getForeground();
6254 	if (endY > 0) {
6255 		int lineCount = isSingleLine() ? 1 : content.getLineCount();
6256 		int x = leftMargin - horizontalScrollOffset;
6257 		for (int i = startLine; y < endY && i < lineCount; i++) {
6258 			y += renderer.drawLine(i, x, y, gc, background, foreground);
6259 		}
6260 		if (y < endY) {
6261 			gc.setBackground(background);
6262 			drawBackground(gc, 0, y, clientAreaWidth, endY - y);
6263 		}
6264 	}
6265 	if (blockSelection && blockXLocation != -1) {
6266 		gc.setBackground(getSelectionBackground());
6267 		Rectangle rect = getBlockSelectionRectangle();
6268 		gc.drawRectangle(rect.x, rect.y, Math.max(1, rect.width - 1), Math.max(1, rect.height - 1));
6269 		gc.setAdvanced(true);
6270 		if (gc.getAdvanced()) {
6271 			gc.setAlpha(100);
6272 			gc.fillRectangle(rect);
6273 			gc.setAdvanced(false);
6274 		}
6275 	}
6276 
6277 	// fill the margin background
6278 	gc.setBackground(marginColor != null ? marginColor : background);
6279 	if (topMargin > 0) {
6280 		drawBackground(gc, 0, 0, clientAreaWidth, topMargin);
6281 	}
6282 	if (bottomMargin > 0) {
6283 		drawBackground(gc, 0, clientAreaHeight - bottomMargin, clientAreaWidth, bottomMargin);
6284 	}
6285 	if (leftMargin - alignmentMargin > 0) {
6286 		drawBackground(gc, 0, 0, leftMargin - alignmentMargin, clientAreaHeight);
6287 	}
6288 	if (rightMargin > 0) {
6289 		drawBackground(gc, clientAreaWidth - rightMargin, 0, rightMargin, clientAreaHeight);
6290 	}
6291 }
6292 /**
6293  * Recalculates the scroll bars. Rewraps all lines when in word
6294  * wrap mode.
6295  *
6296  * @param event resize event
6297  */
handleResize(Event event)6298 void handleResize(Event event) {
6299 	int oldHeight = clientAreaHeight;
6300 	int oldWidth = clientAreaWidth;
6301 	Rectangle clientArea = getClientArea();
6302 	clientAreaHeight = clientArea.height;
6303 	clientAreaWidth = clientArea.width;
6304 	if (!alwaysShowScroll && ignoreResize != 0) return;
6305 
6306 	redrawMargins(oldHeight, oldWidth);
6307 	if (wordWrap) {
6308 		if (oldWidth != clientAreaWidth) {
6309 			renderer.reset(0, content.getLineCount());
6310 			verticalScrollOffset = -1;
6311 			renderer.calculateIdle();
6312 			super.redraw();
6313 		}
6314 		if (oldHeight != clientAreaHeight) {
6315 			if (oldHeight == 0) topIndexY = 0;
6316 			setScrollBars(true);
6317 		}
6318 		setCaretLocation();
6319 	} else  {
6320 		renderer.calculateClientArea();
6321 		setScrollBars(true);
6322 		claimRightFreeSpace();
6323 		// StyledText allows any value for horizontalScrollOffset when clientArea is zero
6324 		// in setHorizontalPixel() and setHorisontalOffset(). Fixes bug 168429.
6325 		if (clientAreaWidth != 0) {
6326 			ScrollBar horizontalBar = getHorizontalBar();
6327 			if (horizontalBar != null && horizontalBar.getVisible()) {
6328 				if (horizontalScrollOffset != horizontalBar.getSelection()) {
6329 					horizontalBar.setSelection(horizontalScrollOffset);
6330 					horizontalScrollOffset = horizontalBar.getSelection();
6331 				}
6332 			}
6333 		}
6334 	}
6335 	updateCaretVisibility();
6336 	claimBottomFreeSpace();
6337 	setAlignment();
6338 	//TODO FIX TOP INDEX DURING RESIZE
6339 //	if (oldHeight != clientAreaHeight || wordWrap) {
6340 //		calculateTopIndex(0);
6341 //	}
6342 }
6343 /**
6344  * Updates the caret position and selection and the scroll bars to reflect
6345  * the content change.
6346  */
handleTextChanged(TextChangedEvent event)6347 void handleTextChanged(TextChangedEvent event) {
6348 	int offset = ime.getCompositionOffset();
6349 	if (offset != -1 && lastTextChangeStart < offset) {
6350 		ime.setCompositionOffset(offset + lastTextChangeNewCharCount - lastTextChangeReplaceCharCount);
6351 	}
6352 	int firstLine = content.getLineAtOffset(lastTextChangeStart);
6353 	resetCache(firstLine, 0);
6354 	if (!isFixedLineHeight() && topIndex > firstLine) {
6355 		topIndex = firstLine;
6356 		if (topIndex < 0) {
6357 			// TODO: This logging is in place to determine why topIndex is getting set to negative values.
6358 			// It should be deleted once we fix the root cause of this issue. See bug 487254 for details.
6359 			System.err.println("StyledText: topIndex was " + topIndex
6360 					+ ", lastTextChangeStart = " + lastTextChangeStart
6361 					+ ", content.getClass() = " + content.getClass()
6362 					);
6363 			topIndex = 0;
6364 		}
6365 		topIndexY = 0;
6366 		super.redraw();
6367 	} else {
6368 		int lastLine = firstLine + lastTextChangeNewLineCount;
6369 		int firstLineTop = getLinePixel(firstLine);
6370 		int newLastLineBottom = getLinePixel(lastLine + 1);
6371 		if (lastLineBottom != newLastLineBottom) {
6372 			super.redraw();
6373 		} else {
6374 			super.redraw(0, firstLineTop, clientAreaWidth, newLastLineBottom - firstLineTop, false);
6375 			redrawLinesBullet(renderer.redrawLines);
6376 		}
6377 	}
6378 	renderer.redrawLines = null;
6379 	// update selection/caret location after styles have been changed.
6380 	// otherwise any text measuring could be incorrect
6381 	//
6382 	// also, this needs to be done after all scrolling. Otherwise,
6383 	// selection redraw would be flushed during scroll which is wrong.
6384 	// in some cases new text would be drawn in scroll source area even
6385 	// though the intent is to scroll it.
6386 	if (!(blockSelection && blockXLocation != -1)) {
6387 		updateSelection(lastTextChangeStart, lastTextChangeReplaceCharCount, lastTextChangeNewCharCount);
6388 	}
6389 	if (lastTextChangeReplaceLineCount > 0 || wordWrap || visualWrap) {
6390 		claimBottomFreeSpace();
6391 	}
6392 	if (lastTextChangeReplaceCharCount > 0) {
6393 		claimRightFreeSpace();
6394 	}
6395 
6396 	sendAccessibleTextChanged(lastTextChangeStart, lastTextChangeNewCharCount, 0);
6397 	lastCharCount += lastTextChangeNewCharCount;
6398 	lastCharCount -= lastTextChangeReplaceCharCount;
6399 	setAlignment();
6400 }
6401 /**
6402  * Updates the screen to reflect a pending content change.
6403  *
6404  * @param event .start the start offset of the change
6405  * @param event .newText text that is going to be inserted or empty String
6406  *	if no text will be inserted
6407  * @param event .replaceCharCount length of text that is going to be replaced
6408  * @param event .newCharCount length of text that is going to be inserted
6409  * @param event .replaceLineCount number of lines that are going to be replaced
6410  * @param event .newLineCount number of new lines that are going to be inserted
6411  */
handleTextChanging(TextChangingEvent event)6412 void handleTextChanging(TextChangingEvent event) {
6413 	if (event.replaceCharCount < 0) {
6414 		event.start += event.replaceCharCount;
6415 		event.replaceCharCount *= -1;
6416 	}
6417 	lastTextChangeStart = event.start;
6418 	lastTextChangeNewLineCount = event.newLineCount;
6419 	lastTextChangeNewCharCount = event.newCharCount;
6420 	lastTextChangeReplaceLineCount = event.replaceLineCount;
6421 	lastTextChangeReplaceCharCount = event.replaceCharCount;
6422 	int lineIndex = content.getLineAtOffset(event.start);
6423 	int srcY = getLinePixel(lineIndex + event.replaceLineCount + 1);
6424 	int destY = getLinePixel(lineIndex + 1) + event.newLineCount * renderer.getLineHeight();
6425 	lastLineBottom = destY;
6426 	if (srcY < 0 && destY < 0) {
6427 		lastLineBottom += srcY - destY;
6428 		verticalScrollOffset += destY - srcY;
6429 		calculateTopIndex(destY - srcY);
6430 		setScrollBars(true);
6431 	} else {
6432 		scrollText(srcY, destY);
6433 	}
6434 	sendAccessibleTextChanged(lastTextChangeStart, 0, lastTextChangeReplaceCharCount);
6435 	renderer.textChanging(event);
6436 
6437 	// Update the caret offset if it is greater than the length of the content.
6438 	// This is necessary since style range API may be called between the
6439 	// handleTextChanging and handleTextChanged events and this API sets the
6440 	// caretOffset.
6441 	int newEndOfText = content.getCharCount() - event.replaceCharCount + event.newCharCount;
6442 	if (caretOffset > newEndOfText) setCaretOffset(newEndOfText, SWT.DEFAULT);
6443 }
6444 /**
6445  * Called when the widget content is set programmatically, overwriting
6446  * the old content. Resets the caret position, selection and scroll offsets.
6447  * Recalculates the content width and scroll bars. Redraws the widget.
6448  *
6449  * @param event text change event.
6450  */
handleTextSet(TextChangedEvent event)6451 void handleTextSet(TextChangedEvent event) {
6452 	reset();
6453 	int newCharCount = getCharCount();
6454 	sendAccessibleTextChanged(0, newCharCount, lastCharCount);
6455 	lastCharCount = newCharCount;
6456 	setAlignment();
6457 }
6458 /**
6459  * Called when a traversal key is pressed.
6460  * Allow tab next traversal to occur when the widget is in single
6461  * line mode or in multi line and non-editable mode .
6462  * When in editable multi line mode we want to prevent the tab
6463  * traversal and receive the tab key event instead.
6464  *
6465  * @param event the event
6466  */
handleTraverse(Event event)6467 void handleTraverse(Event event) {
6468 	switch (event.detail) {
6469 		case SWT.TRAVERSE_ESCAPE:
6470 		case SWT.TRAVERSE_PAGE_NEXT:
6471 		case SWT.TRAVERSE_PAGE_PREVIOUS:
6472 			event.doit = true;
6473 			break;
6474 		case SWT.TRAVERSE_RETURN:
6475 		case SWT.TRAVERSE_TAB_NEXT:
6476 		case SWT.TRAVERSE_TAB_PREVIOUS:
6477 			if ((getStyle() & SWT.SINGLE) != 0) {
6478 				event.doit = true;
6479 			} else {
6480 				if (!editable || (event.stateMask & SWT.MODIFIER_MASK) != 0) {
6481 					event.doit = true;
6482 				}
6483 			}
6484 			break;
6485 	}
6486 }
6487 /**
6488  * Scrolls the widget vertically.
6489  */
handleVerticalScroll(Event event)6490 void handleVerticalScroll(Event event) {
6491 	int scrollPixel = getVerticalBar().getSelection() - getVerticalScrollOffset();
6492 	scrollVertical(scrollPixel, false);
6493 }
6494 /**
6495  * Add accessibility support for the widget.
6496  */
initializeAccessible()6497 void initializeAccessible() {
6498 	acc = getAccessible();
6499 
6500 	accAdapter = new AccessibleAdapter() {
6501 		@Override
6502 		public void getName (AccessibleEvent e) {
6503 			String name = null;
6504 			String text = getAssociatedLabel ();
6505 			if (text != null) {
6506 				name = stripMnemonic (text);
6507 			}
6508 			e.result = name;
6509 		}
6510 		@Override
6511 		public void getHelp(AccessibleEvent e) {
6512 			e.result = getToolTipText();
6513 		}
6514 		@Override
6515 		public void getKeyboardShortcut(AccessibleEvent e) {
6516 			String shortcut = null;
6517 			String text = getAssociatedLabel ();
6518 			if (text != null) {
6519 				char mnemonic = _findMnemonic (text);
6520 				if (mnemonic != '\0') {
6521 					shortcut = "Alt+"+mnemonic; //$NON-NLS-1$
6522 				}
6523 			}
6524 			e.result = shortcut;
6525 		}
6526 	};
6527 	acc.addAccessibleListener(accAdapter);
6528 
6529 	accTextExtendedAdapter = new AccessibleTextExtendedAdapter() {
6530 		@Override
6531 		public void getCaretOffset(AccessibleTextEvent e) {
6532 			e.offset = StyledText.this.getCaretOffset();
6533 		}
6534 		@Override
6535 		public void setCaretOffset(AccessibleTextEvent e) {
6536 			StyledText.this.setCaretOffset(e.offset);
6537 			e.result = ACC.OK;
6538 		}
6539 		@Override
6540 		public void getSelectionRange(AccessibleTextEvent e) {
6541 			Point selection = StyledText.this.getSelectionRange();
6542 			e.offset = selection.x;
6543 			e.length = selection.y;
6544 		}
6545 		@Override
6546 		public void addSelection(AccessibleTextEvent e) {
6547 			StyledText st = StyledText.this;
6548 			Point point = st.getSelection();
6549 			if (point.x == point.y) {
6550 				int end = e.end;
6551 				if (end == -1) end = st.getCharCount();
6552 				st.setSelection(e.start, end);
6553 				e.result = ACC.OK;
6554 			}
6555 		}
6556 		@Override
6557 		public void getSelection(AccessibleTextEvent e) {
6558 			StyledText st = StyledText.this;
6559 			if (st.blockSelection && st.blockXLocation != -1) {
6560 				Rectangle rect = st.getBlockSelectionPosition();
6561 				int lineIndex = rect.y + e.index;
6562 				int linePixel = st.getLinePixel(lineIndex);
6563 				e.ranges = getRanges(rect.x, linePixel, rect.width, linePixel);
6564 				if (e.ranges.length > 0) {
6565 					e.start = e.ranges[0];
6566 					e.end = e.ranges[e.ranges.length - 1];
6567 				}
6568 			} else {
6569 				if (e.index == 0) {
6570 					Point point = st.getSelection();
6571 					e.start = point.x;
6572 					e.end = point.y;
6573 					if (e.start > e.end) {
6574 						int temp = e.start;
6575 						e.start = e.end;
6576 						e.end = temp;
6577 					}
6578 				}
6579 			}
6580 		}
6581 		@Override
6582 		public void getSelectionCount(AccessibleTextEvent e) {
6583 			StyledText st = StyledText.this;
6584 			if (st.blockSelection && st.blockXLocation != -1) {
6585 				Rectangle rect = st.getBlockSelectionPosition();
6586 				e.count = rect.height - rect.y + 1;
6587 			} else {
6588 				Point point = st.getSelection();
6589 				e.count = point.x == point.y ? 0 : 1;
6590 			}
6591 		}
6592 		@Override
6593 		public void removeSelection(AccessibleTextEvent e) {
6594 			StyledText st = StyledText.this;
6595 			if (e.index == 0) {
6596 				if (st.blockSelection) {
6597 					st.clearBlockSelection(true, false);
6598 				} else {
6599 					st.clearSelection(false);
6600 				}
6601 				e.result = ACC.OK;
6602 			}
6603 		}
6604 		@Override
6605 		public void setSelection(AccessibleTextEvent e) {
6606 			if (e.index != 0) return;
6607 			StyledText st = StyledText.this;
6608 			Point point = st.getSelection();
6609 			if (point.x == point.y) return;
6610 			int end = e.end;
6611 			if (end == -1) end = st.getCharCount();
6612 			st.setSelection(e.start, end);
6613 			e.result = ACC.OK;
6614 		}
6615 		@Override
6616 		public void getCharacterCount(AccessibleTextEvent e) {
6617 			e.count = StyledText.this.getCharCount();
6618 		}
6619 		@Override
6620 		public void getOffsetAtPoint(AccessibleTextEvent e) {
6621 			StyledText st = StyledText.this;
6622 			Point point = new Point (e.x, e.y);
6623 			Display display = st.getDisplay();
6624 			point = display.map(null, st, point);
6625 			e.offset = st.getOffsetAtPoint(point.x, point.y, null, true);
6626 		}
6627 		@Override
6628 		public void getTextBounds(AccessibleTextEvent e) {
6629 			StyledText st = StyledText.this;
6630 			int start = e.start;
6631 			int end = e.end;
6632 			int contentLength = st.getCharCount();
6633 			start = Math.max(0, Math.min(start, contentLength));
6634 			end = Math.max(0, Math.min(end, contentLength));
6635 			if (start > end) {
6636 				int temp = start;
6637 				start = end;
6638 				end = temp;
6639 			}
6640 			int startLine = st.getLineAtOffset(start);
6641 			int endLine = st.getLineAtOffset(end);
6642 			Rectangle[] rects = new Rectangle[endLine - startLine + 1];
6643 			Rectangle bounds = null;
6644 			int index = 0;
6645 			Display display = st.getDisplay();
6646 			for (int lineIndex = startLine; lineIndex <= endLine; lineIndex++) {
6647 				Rectangle rect = new Rectangle(0, 0, 0, 0);
6648 				rect.y = st.getLinePixel(lineIndex);
6649 				rect.height = st.renderer.getLineHeight(lineIndex);
6650 				if (lineIndex == startLine) {
6651 					rect.x = st.getPointAtOffset(start).x;
6652 				} else {
6653 					rect.x = st.leftMargin - st.horizontalScrollOffset;
6654 				}
6655 				if (lineIndex == endLine) {
6656 					rect.width = st.getPointAtOffset(end).x - rect.x;
6657 				} else {
6658 					TextLayout layout = st.renderer.getTextLayout(lineIndex);
6659 					rect.width = layout.getBounds().width - rect.x;
6660 					st.renderer.disposeTextLayout(layout);
6661 				}
6662 				rects [index++] = rect = display.map(st, null, rect);
6663 				if (bounds == null) {
6664 					bounds = new Rectangle(rect.x, rect.y, rect.width, rect.height);
6665 				} else {
6666 					bounds.add(rect);
6667 				}
6668 			}
6669 			e.rectangles = rects;
6670 			if (bounds != null) {
6671 				e.x = bounds.x;
6672 				e.y = bounds.y;
6673 				e.width = bounds.width;
6674 				e.height = bounds.height;
6675 			}
6676 		}
6677 		int[] getRanges(int left, int top, int right, int bottom) {
6678 			StyledText st = StyledText.this;
6679 			int lineStart = st.getLineIndex(top);
6680 			int lineEnd = st.getLineIndex(bottom);
6681 			int count = lineEnd - lineStart + 1;
6682 			int[] ranges = new int [count * 2];
6683 			int index = 0;
6684 			for (int lineIndex = lineStart; lineIndex <= lineEnd; lineIndex++) {
6685 				String line = st.content.getLine(lineIndex);
6686 				int lineOffset = st.content.getOffsetAtLine(lineIndex);
6687 				int lineEndOffset = lineOffset + line.length();
6688 				int linePixel = st.getLinePixel(lineIndex);
6689 				int start = st.getOffsetAtPoint(left, linePixel, null, true);
6690 				if (start == -1) {
6691 					start = left < st.leftMargin ? lineOffset : lineEndOffset;
6692 				}
6693 				int[] trailing = new int[1];
6694 				int end = st.getOffsetAtPoint(right, linePixel, trailing, true);
6695 				if (end == -1) {
6696 					end = right < st.leftMargin ? lineOffset : lineEndOffset;
6697 				} else {
6698 					end += trailing[0];
6699 				}
6700 				if (start > end) {
6701 					int temp = start;
6702 					start = end;
6703 					end = temp;
6704 				}
6705 				ranges[index++] = start;
6706 				ranges[index++] = end;
6707 			}
6708 			return ranges;
6709 		}
6710 		@Override
6711 		public void getRanges(AccessibleTextEvent e) {
6712 			StyledText st = StyledText.this;
6713 			Point point = new Point (e.x, e.y);
6714 			Display display = st.getDisplay();
6715 			point = display.map(null, st, point);
6716 			e.ranges = getRanges(point.x, point.y, point.x + e.width, point.y + e.height);
6717 			if (e.ranges.length > 0) {
6718 				e.start = e.ranges[0];
6719 				e.end = e.ranges[e.ranges.length - 1];
6720 			}
6721 		}
6722 		@Override
6723 		public void getText(AccessibleTextEvent e) {
6724 			StyledText st = StyledText.this;
6725 			int start = e.start;
6726 			int end = e.end;
6727 			int contentLength = st.getCharCount();
6728 			if (end == -1) end = contentLength;
6729 			start = Math.max(0, Math.min(start, contentLength));
6730 			end = Math.max(0, Math.min(end, contentLength));
6731 			if (start > end) {
6732 				int temp = start;
6733 				start = end;
6734 				end = temp;
6735 			}
6736 			int count = e.count;
6737 			switch (e.type) {
6738 				case ACC.TEXT_BOUNDARY_ALL:
6739 					//nothing to do
6740 					break;
6741 				case ACC.TEXT_BOUNDARY_CHAR: {
6742 					int newCount = 0;
6743 					if (count > 0) {
6744 						while (count-- > 0) {
6745 							int newEnd = st.getWordNext(end, SWT.MOVEMENT_CLUSTER);
6746 							if (newEnd == contentLength) break;
6747 							if (newEnd == end) break;
6748 							end = newEnd;
6749 							newCount++;
6750 						}
6751 						start = end;
6752 						end = st.getWordNext(end, SWT.MOVEMENT_CLUSTER);
6753 					} else {
6754 						while (count++ < 0) {
6755 							int newStart = st.getWordPrevious(start, SWT.MOVEMENT_CLUSTER);
6756 							if (newStart == start) break;
6757 							start = newStart;
6758 							newCount--;
6759 						}
6760 						end = st.getWordNext(start, SWT.MOVEMENT_CLUSTER);
6761 					}
6762 					count = newCount;
6763 					break;
6764 				}
6765 				case ACC.TEXT_BOUNDARY_WORD: {
6766 					int newCount = 0;
6767 					if (count > 0) {
6768 						while (count-- > 0) {
6769 							int newEnd = st.getWordNext(end, SWT.MOVEMENT_WORD_START, true);
6770 							if (newEnd == end) break;
6771 							newCount++;
6772 							end = newEnd;
6773 						}
6774 						start = end;
6775 						end = st.getWordNext(start, SWT.MOVEMENT_WORD_END, true);
6776 					} else {
6777 						if (st.getWordPrevious(Math.min(start + 1, contentLength), SWT.MOVEMENT_WORD_START, true) == start) {
6778 							//start is a word start already
6779 							count++;
6780 						}
6781 						while (count <= 0) {
6782 							int newStart = st.getWordPrevious(start, SWT.MOVEMENT_WORD_START, true);
6783 							if (newStart == start) break;
6784 							count++;
6785 							start = newStart;
6786 							if (count != 0) newCount--;
6787 						}
6788 						if (count <= 0 && start == 0) {
6789 							end = start;
6790 						} else {
6791 							end = st.getWordNext(start, SWT.MOVEMENT_WORD_END, true);
6792 						}
6793 					}
6794 					count = newCount;
6795 					break;
6796 				}
6797 				case ACC.TEXT_BOUNDARY_LINE:
6798 					//TODO implement line
6799 				case ACC.TEXT_BOUNDARY_PARAGRAPH:
6800 				case ACC.TEXT_BOUNDARY_SENTENCE: {
6801 					int offset = count > 0 ? end : start;
6802 					int lineIndex = st.getLineAtOffset(offset) + count;
6803 					lineIndex = Math.max(0, Math.min(lineIndex, st.getLineCount() - 1));
6804 					start = st.getOffsetAtLine(lineIndex);
6805 					String line = st.getLine(lineIndex);
6806 					end = start + line.length();
6807 					count = lineIndex - st.getLineAtOffset(offset);
6808 					break;
6809 				}
6810 			}
6811 			e.start = start;
6812 			e.end = end;
6813 			e.count = count;
6814 			e.result = st.content.getTextRange(start, end - start);
6815 		}
6816 		@Override
6817 		public void getVisibleRanges(AccessibleTextEvent e) {
6818 			e.ranges = getRanges(leftMargin, topMargin, clientAreaWidth - rightMargin, clientAreaHeight - bottomMargin);
6819 			if (e.ranges.length > 0) {
6820 				e.start = e.ranges[0];
6821 				e.end = e.ranges[e.ranges.length - 1];
6822 			}
6823 		}
6824 		@Override
6825 		public void scrollText(AccessibleTextEvent e) {
6826 			StyledText st = StyledText.this;
6827 			int topPixel = getTopPixel(), horizontalPixel = st.getHorizontalPixel();
6828 			switch (e.type) {
6829 				case ACC.SCROLL_TYPE_ANYWHERE:
6830 				case ACC.SCROLL_TYPE_TOP_LEFT:
6831 				case ACC.SCROLL_TYPE_LEFT_EDGE:
6832 				case ACC.SCROLL_TYPE_TOP_EDGE: {
6833 					Rectangle rect = st.getBoundsAtOffset(e.start);
6834 					if (e.type != ACC.SCROLL_TYPE_TOP_EDGE) {
6835 						horizontalPixel = horizontalPixel + rect.x - st.leftMargin;
6836 					}
6837 					if (e.type != ACC.SCROLL_TYPE_LEFT_EDGE) {
6838 						topPixel = topPixel + rect.y - st.topMargin;
6839 					}
6840 					break;
6841 				}
6842 				case ACC.SCROLL_TYPE_BOTTOM_RIGHT:
6843 				case ACC.SCROLL_TYPE_BOTTOM_EDGE:
6844 				case ACC.SCROLL_TYPE_RIGHT_EDGE: {
6845 					Rectangle rect = st.getBoundsAtOffset(e.end - 1);
6846 					if (e.type != ACC.SCROLL_TYPE_BOTTOM_EDGE) {
6847 						horizontalPixel = horizontalPixel - st.clientAreaWidth + rect.x + rect.width + st.rightMargin;
6848 					}
6849 					if (e.type != ACC.SCROLL_TYPE_RIGHT_EDGE) {
6850 						topPixel = topPixel - st.clientAreaHeight + rect.y +rect.height + st.bottomMargin;
6851 					}
6852 					break;
6853 				}
6854 				case ACC.SCROLL_TYPE_POINT: {
6855 					Point point = new Point(e.x, e.y);
6856 					Display display = st.getDisplay();
6857 					point = display.map(null, st, point);
6858 					Rectangle rect = st.getBoundsAtOffset(e.start);
6859 					topPixel = topPixel - point.y + rect.y;
6860 					horizontalPixel = horizontalPixel - point.x + rect.x;
6861 					break;
6862 				}
6863 			}
6864 			st.setTopPixel(topPixel);
6865 			st.setHorizontalPixel(horizontalPixel);
6866 			e.result = ACC.OK;
6867 		}
6868 	};
6869 	acc.addAccessibleTextListener(accTextExtendedAdapter);
6870 
6871 	accEditableTextListener = new AccessibleEditableTextListener() {
6872 		@Override
6873 		public void setTextAttributes(AccessibleTextAttributeEvent e) {
6874 			// This method must be implemented by the application
6875 			e.result = ACC.OK;
6876 		}
6877 		@Override
6878 		public void replaceText(AccessibleEditableTextEvent e) {
6879 			StyledText st = StyledText.this;
6880 			st.replaceTextRange(e.start, e.end - e.start, e.string);
6881 			e.result = ACC.OK;
6882 		}
6883 		@Override
6884 		public void pasteText(AccessibleEditableTextEvent e) {
6885 			StyledText st = StyledText.this;
6886 			st.setSelection(e.start);
6887 			st.paste();
6888 			e.result = ACC.OK;
6889 		}
6890 		@Override
6891 		public void cutText(AccessibleEditableTextEvent e) {
6892 			StyledText st = StyledText.this;
6893 			st.setSelection(e.start, e.end);
6894 			st.cut();
6895 			e.result = ACC.OK;
6896 		}
6897 		@Override
6898 		public void copyText(AccessibleEditableTextEvent e) {
6899 			StyledText st = StyledText.this;
6900 			st.setSelection(e.start, e.end);
6901 			st.copy();
6902 			e.result = ACC.OK;
6903 		}
6904 	};
6905 	acc.addAccessibleEditableTextListener(accEditableTextListener);
6906 
6907 	accAttributeAdapter = new AccessibleAttributeAdapter() {
6908 		@Override
6909 		public void getAttributes(AccessibleAttributeEvent e) {
6910 			StyledText st = StyledText.this;
6911 			e.leftMargin = st.getLeftMargin();
6912 			e.topMargin = st.getTopMargin();
6913 			e.rightMargin = st.getRightMargin();
6914 			e.bottomMargin = st.getBottomMargin();
6915 			e.tabStops = st.getTabStops();
6916 			e.justify = st.getJustify();
6917 			e.alignment = st.getAlignment();
6918 			e.indent = st.getIndent();
6919 		}
6920 		@Override
6921 		public void getTextAttributes(AccessibleTextAttributeEvent e) {
6922 			StyledText st = StyledText.this;
6923 			int contentLength = st.getCharCount();
6924 			if (!isListening(ST.LineGetStyle) && st.renderer.styleCount == 0) {
6925 				e.start = 0;
6926 				e.end = contentLength;
6927 				e.textStyle = new TextStyle(st.getFont(), st.foreground, st.background);
6928 				return;
6929 			}
6930 			int offset = Math.max(0, Math.min(e.offset, contentLength - 1));
6931 			int lineIndex = st.getLineAtOffset(offset);
6932 			int lineOffset = st.getOffsetAtLine(lineIndex);
6933 			int lineCount = st.getLineCount();
6934 			offset = offset - lineOffset;
6935 
6936 			TextLayout layout = st.renderer.getTextLayout(lineIndex);
6937 			int lineLength = layout.getText().length();
6938 			if (lineLength > 0) {
6939 				e.textStyle = layout.getStyle(Math.max(0, Math.min(offset, lineLength - 1)));
6940 			}
6941 
6942 			// If no override info available, use defaults. Don't supply default colors, though.
6943 			if (e.textStyle == null) {
6944 				e.textStyle = new TextStyle(st.getFont(), st.foreground, st.background);
6945 			} else {
6946 				if (e.textStyle.foreground == null || e.textStyle.background == null || e.textStyle.font == null) {
6947 					TextStyle textStyle = new TextStyle(e.textStyle);
6948 					if (textStyle.foreground == null) textStyle.foreground = st.foreground;
6949 					if (textStyle.background == null) textStyle.background = st.background;
6950 					if (textStyle.font == null) textStyle.font = st.getFont();
6951 					e.textStyle = textStyle;
6952 				}
6953 			}
6954 
6955 			//offset at line delimiter case
6956 			if (offset >= lineLength) {
6957 				e.start = lineOffset + lineLength;
6958 				if (lineIndex + 1 < lineCount) {
6959 					e.end = st.getOffsetAtLine(lineIndex + 1);
6960 				} else  {
6961 					e.end = contentLength;
6962 				}
6963 				return;
6964 			}
6965 
6966 			int[] ranges = layout.getRanges();
6967 			st.renderer.disposeTextLayout(layout);
6968 			int index = 0;
6969 			int end = 0;
6970 			while (index < ranges.length) {
6971 				int styleStart = ranges[index++];
6972 				int styleEnd = ranges[index++];
6973 				if (styleStart <= offset && offset <= styleEnd) {
6974 					e.start = lineOffset + styleStart;
6975 					e.end = lineOffset + styleEnd + 1;
6976 					return;
6977 				}
6978 				if (styleStart > offset) {
6979 					e.start = lineOffset + end;
6980 					e.end = lineOffset + styleStart;
6981 					return;
6982 				}
6983 				end = styleEnd + 1;
6984 			}
6985 			if (index == ranges.length) {
6986 				e.start = lineOffset + end;
6987 				if (lineIndex + 1 < lineCount) {
6988 					e.end = st.getOffsetAtLine(lineIndex + 1);
6989 				} else  {
6990 					e.end = contentLength;
6991 				}
6992 			}
6993 		}
6994 	};
6995 	acc.addAccessibleAttributeListener(accAttributeAdapter);
6996 
6997 	accControlAdapter = new AccessibleControlAdapter() {
6998 		@Override
6999 		public void getRole(AccessibleControlEvent e) {
7000 			e.detail = ACC.ROLE_TEXT;
7001 		}
7002 		@Override
7003 		public void getState(AccessibleControlEvent e) {
7004 			int state = 0;
7005 			if (isEnabled()) state |= ACC.STATE_FOCUSABLE;
7006 			if (isFocusControl()) state |= ACC.STATE_FOCUSED;
7007 			if (!isVisible()) state |= ACC.STATE_INVISIBLE;
7008 			if (!getEditable()) state |= ACC.STATE_READONLY;
7009 			if (isSingleLine()) state |= ACC.STATE_SINGLELINE;
7010 			else state |= ACC.STATE_MULTILINE;
7011 			e.detail = state;
7012 		}
7013 		@Override
7014 		public void getValue(AccessibleControlEvent e) {
7015 			e.result = StyledText.this.getText();
7016 		}
7017 	};
7018 	acc.addAccessibleControlListener(accControlAdapter);
7019 
7020 	addListener(SWT.FocusIn, event -> acc.setFocus(ACC.CHILDID_SELF));
7021 }
7022 
7023 @Override
dispose()7024 public void dispose() {
7025 	/*
7026 	 * Note: It is valid to attempt to dispose a widget more than once.
7027 	 * Added check for this.
7028 	 */
7029 	if (!isDisposed()) {
7030 		acc.removeAccessibleControlListener(accControlAdapter);
7031 		acc.removeAccessibleAttributeListener(accAttributeAdapter);
7032 		acc.removeAccessibleEditableTextListener(accEditableTextListener);
7033 		acc.removeAccessibleTextListener(accTextExtendedAdapter);
7034 		acc.removeAccessibleListener(accAdapter);
7035 	}
7036 	super.dispose();
7037 }
7038 
7039 /*
7040  * Return the Label immediately preceding the receiver in the z-order,
7041  * or null if none.
7042  */
getAssociatedLabel()7043 String getAssociatedLabel () {
7044 	Control[] siblings = getParent ().getChildren ();
7045 	for (int i = 0; i < siblings.length; i++) {
7046 		if (siblings [i] == StyledText.this) {
7047 			if (i > 0) {
7048 				Control sibling = siblings [i-1];
7049 				if (sibling instanceof Label) return ((Label) sibling).getText();
7050 				if (sibling instanceof CLabel) return ((CLabel) sibling).getText();
7051 			}
7052 			break;
7053 		}
7054 	}
7055 	return null;
7056 }
stripMnemonic(String string)7057 String stripMnemonic (String string) {
7058 	int index = 0;
7059 	int length = string.length ();
7060 	do {
7061 		while ((index < length) && (string.charAt (index) != '&')) index++;
7062 		if (++index >= length) return string;
7063 		if (string.charAt (index) != '&') {
7064 			return string.substring(0, index-1) + string.substring(index, length);
7065 		}
7066 		index++;
7067 	} while (index < length);
7068 	return string;
7069 }
7070 /*
7071  * Return the lowercase of the first non-'&' character following
7072  * an '&' character in the given string. If there are no '&'
7073  * characters in the given string, return '\0'.
7074  */
_findMnemonic(String string)7075 char _findMnemonic (String string) {
7076 	if (string == null) return '\0';
7077 	int index = 0;
7078 	int length = string.length ();
7079 	do {
7080 		while (index < length && string.charAt (index) != '&') index++;
7081 		if (++index >= length) return '\0';
7082 		if (string.charAt (index) != '&') return Character.toLowerCase (string.charAt (index));
7083 		index++;
7084 	} while (index < length);
7085 	return '\0';
7086 }
7087 /**
7088  * Executes the action.
7089  *
7090  * @param action one of the actions defined in ST.java
7091  */
invokeAction(int action)7092 public void invokeAction(int action) {
7093 	checkWidget();
7094 	if (blockSelection && invokeBlockAction(action)) return;
7095 	updateCaretDirection = true;
7096 	switch (action) {
7097 		// Navigation
7098 		case ST.LINE_UP:
7099 			doLineUp(false);
7100 			clearSelection(true);
7101 			break;
7102 		case ST.LINE_DOWN:
7103 			doLineDown(false);
7104 			clearSelection(true);
7105 			break;
7106 		case ST.LINE_START:
7107 			doLineStart();
7108 			clearSelection(true);
7109 			break;
7110 		case ST.LINE_END:
7111 			doLineEnd();
7112 			clearSelection(true);
7113 			break;
7114 		case ST.COLUMN_PREVIOUS:
7115 			doCursorPrevious();
7116 			clearSelection(true);
7117 			break;
7118 		case ST.COLUMN_NEXT:
7119 			doCursorNext();
7120 			clearSelection(true);
7121 			break;
7122 		case ST.PAGE_UP:
7123 			doPageUp(false, -1);
7124 			clearSelection(true);
7125 			break;
7126 		case ST.PAGE_DOWN:
7127 			doPageDown(false, -1);
7128 			clearSelection(true);
7129 			break;
7130 		case ST.WORD_PREVIOUS:
7131 			doWordPrevious();
7132 			clearSelection(true);
7133 			break;
7134 		case ST.WORD_NEXT:
7135 			doWordNext();
7136 			clearSelection(true);
7137 			break;
7138 		case ST.TEXT_START:
7139 			doContentStart();
7140 			clearSelection(true);
7141 			break;
7142 		case ST.TEXT_END:
7143 			doContentEnd();
7144 			clearSelection(true);
7145 			break;
7146 		case ST.WINDOW_START:
7147 			doPageStart();
7148 			clearSelection(true);
7149 			break;
7150 		case ST.WINDOW_END:
7151 			doPageEnd();
7152 			clearSelection(true);
7153 			break;
7154 		// Selection
7155 		case ST.SELECT_LINE_UP:
7156 			doSelectionLineUp();
7157 			break;
7158 		case ST.SELECT_ALL:
7159 			selectAll();
7160 			break;
7161 		case ST.SELECT_LINE_DOWN:
7162 			doSelectionLineDown();
7163 			break;
7164 		case ST.SELECT_LINE_START:
7165 			doLineStart();
7166 			doSelection(ST.COLUMN_PREVIOUS);
7167 			break;
7168 		case ST.SELECT_LINE_END:
7169 			doLineEnd();
7170 			doSelection(ST.COLUMN_NEXT);
7171 			break;
7172 		case ST.SELECT_COLUMN_PREVIOUS:
7173 			doSelectionCursorPrevious();
7174 			doSelection(ST.COLUMN_PREVIOUS);
7175 			break;
7176 		case ST.SELECT_COLUMN_NEXT:
7177 			doSelectionCursorNext();
7178 			doSelection(ST.COLUMN_NEXT);
7179 			break;
7180 		case ST.SELECT_PAGE_UP:
7181 			doSelectionPageUp(-1);
7182 			break;
7183 		case ST.SELECT_PAGE_DOWN:
7184 			doSelectionPageDown(-1);
7185 			break;
7186 		case ST.SELECT_WORD_PREVIOUS:
7187 			doSelectionWordPrevious();
7188 			doSelection(ST.COLUMN_PREVIOUS);
7189 			break;
7190 		case ST.SELECT_WORD_NEXT:
7191 			doSelectionWordNext();
7192 			doSelection(ST.COLUMN_NEXT);
7193 			break;
7194 		case ST.SELECT_TEXT_START:
7195 			doContentStart();
7196 			doSelection(ST.COLUMN_PREVIOUS);
7197 			break;
7198 		case ST.SELECT_TEXT_END:
7199 			doContentEnd();
7200 			doSelection(ST.COLUMN_NEXT);
7201 			break;
7202 		case ST.SELECT_WINDOW_START:
7203 			doPageStart();
7204 			doSelection(ST.COLUMN_PREVIOUS);
7205 			break;
7206 		case ST.SELECT_WINDOW_END:
7207 			doPageEnd();
7208 			doSelection(ST.COLUMN_NEXT);
7209 			break;
7210 		// Modification
7211 		case ST.CUT:
7212 			cut();
7213 			break;
7214 		case ST.COPY:
7215 			copy();
7216 			break;
7217 		case ST.PASTE:
7218 			paste();
7219 			break;
7220 		case ST.DELETE_PREVIOUS:
7221 			doBackspace();
7222 			break;
7223 		case ST.DELETE_NEXT:
7224 			doDelete();
7225 			break;
7226 		case ST.DELETE_WORD_PREVIOUS:
7227 			doDeleteWordPrevious();
7228 			break;
7229 		case ST.DELETE_WORD_NEXT:
7230 			doDeleteWordNext();
7231 			break;
7232 		// Miscellaneous
7233 		case ST.TOGGLE_OVERWRITE:
7234 			overwrite = !overwrite;		// toggle insert/overwrite mode
7235 			break;
7236 		case ST.TOGGLE_BLOCKSELECTION:
7237 			setBlockSelection(!blockSelection);
7238 			break;
7239 	}
7240 }
7241 /**
7242 * Returns true if an action should not be performed when block
7243 * selection in active
7244 */
invokeBlockAction(int action)7245 boolean invokeBlockAction(int action) {
7246 	switch (action) {
7247 		// Navigation
7248 		case ST.LINE_UP:
7249 		case ST.LINE_DOWN:
7250 		case ST.LINE_START:
7251 		case ST.LINE_END:
7252 		case ST.COLUMN_PREVIOUS:
7253 		case ST.COLUMN_NEXT:
7254 		case ST.PAGE_UP:
7255 		case ST.PAGE_DOWN:
7256 		case ST.WORD_PREVIOUS:
7257 		case ST.WORD_NEXT:
7258 		case ST.TEXT_START:
7259 		case ST.TEXT_END:
7260 		case ST.WINDOW_START:
7261 		case ST.WINDOW_END:
7262 			clearBlockSelection(false, false);
7263 			return false;
7264 		// Selection
7265 		case ST.SELECT_LINE_UP:
7266 			doBlockLineVertical(true);
7267 			return true;
7268 		case ST.SELECT_LINE_DOWN:
7269 			doBlockLineVertical(false);
7270 			return true;
7271 		case ST.SELECT_LINE_START:
7272 			doBlockLineHorizontal(false);
7273 			return true;
7274 		case ST.SELECT_LINE_END:
7275 			doBlockLineHorizontal(true);
7276 			return false;
7277 		case ST.SELECT_COLUMN_PREVIOUS:
7278 			doBlockColumn(false);
7279 			return true;
7280 		case ST.SELECT_COLUMN_NEXT:
7281 			doBlockColumn(true);
7282 			return true;
7283 		case ST.SELECT_WORD_PREVIOUS:
7284 			doBlockWord(false);
7285 			return true;
7286 		case ST.SELECT_WORD_NEXT:
7287 			doBlockWord(true);
7288 			return true;
7289 		case ST.SELECT_ALL:
7290 			return false;
7291 		case ST.SELECT_TEXT_START:
7292 			doBlockContentStartEnd(false);
7293 			break;
7294 		case ST.SELECT_TEXT_END:
7295 			doBlockContentStartEnd(true);
7296 			break;
7297 		case ST.SELECT_PAGE_UP:
7298 		case ST.SELECT_PAGE_DOWN:
7299 		case ST.SELECT_WINDOW_START:
7300 		case ST.SELECT_WINDOW_END:
7301 			//blocked actions
7302 			return true;
7303 		// Modification
7304 		case ST.CUT:
7305 		case ST.COPY:
7306 		case ST.PASTE:
7307 			return false;
7308 		case ST.DELETE_PREVIOUS:
7309 		case ST.DELETE_NEXT:
7310 			if (blockXLocation != -1) {
7311 				insertBlockSelectionText((char)0, action);
7312 				return true;
7313 			}
7314 			return false;
7315 		case ST.DELETE_WORD_PREVIOUS:
7316 		case ST.DELETE_WORD_NEXT:
7317 			//blocked actions
7318 			return blockXLocation != -1;
7319 	}
7320 	return false;
7321 }
isBidiCaret()7322 boolean isBidiCaret() {
7323 	return BidiUtil.isBidiPlatform();
7324 }
isFixedLineHeight()7325 boolean isFixedLineHeight() {
7326 	return !isWordWrap() && lineSpacing == 0 && renderer.lineSpacingProvider == null && !hasStyleWithVariableHeight && !hasVerticalIndent;
7327 }
7328 /**
7329  * Returns whether the given offset is inside a multi byte line delimiter.
7330  * Example:
7331  * "Line1\r\n" isLineDelimiter(5) == false but isLineDelimiter(6) == true
7332  *
7333  * @return true if the given offset is inside a multi byte line delimiter.
7334  * false if the given offset is before or after a line delimiter.
7335  */
isLineDelimiter(int offset)7336 boolean isLineDelimiter(int offset) {
7337 	int line = content.getLineAtOffset(offset);
7338 	int lineOffset = content.getOffsetAtLine(line);
7339 	int offsetInLine = offset - lineOffset;
7340 	// offsetInLine will be greater than line length if the line
7341 	// delimiter is longer than one character and the offset is set
7342 	// in between parts of the line delimiter.
7343 	return offsetInLine > content.getLine(line).length();
7344 }
7345 /**
7346  * Returns whether the widget is mirrored (right oriented/right to left
7347  * writing order).
7348  *
7349  * @return isMirrored true=the widget is right oriented, false=the widget
7350  * 	is left oriented
7351  */
isMirrored()7352 boolean isMirrored() {
7353 	return (getStyle() & SWT.MIRRORED) != 0;
7354 }
7355 /**
7356  * Returns <code>true</code> if any text in the widget is selected,
7357  * and <code>false</code> otherwise.
7358  *
7359  * @return the text selection state
7360  * @exception SWTException <ul>
7361  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7362  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7363  * </ul>
7364  *
7365  * @since 3.103
7366  */
isTextSelected()7367 public boolean isTextSelected() {
7368 	checkWidget();
7369 	if (blockSelection && blockXLocation != -1) {
7370 		Rectangle rect = getBlockSelectionPosition();
7371 		return !rect.isEmpty();
7372 	}
7373 	return selection.y != selection.x;
7374 }
7375 /**
7376  * Returns whether the widget can have only one line.
7377  *
7378  * @return true if widget can have only one line, false if widget can have
7379  * 	multiple lines
7380  */
isSingleLine()7381 boolean isSingleLine() {
7382 	return (getStyle() & SWT.SINGLE) != 0;
7383 }
7384 
7385 /**
7386  * Sends the specified verify event, replace/insert text as defined by
7387  * the event and send a modify event.
7388  *
7389  * @param event	the text change event.
7390  *	<ul>
7391  *	<li>event.start - the replace start offset</li>
7392  * 	<li>event.end - the replace end offset</li>
7393  * 	<li>event.text - the new text</li>
7394  *	</ul>
7395  * @param updateCaret whether or not he caret should be set behind
7396  *	the new text
7397  */
modifyContent(Event event, boolean updateCaret)7398 void modifyContent(Event event, boolean updateCaret) {
7399 	event.doit = true;
7400 	notifyListeners(SWT.Verify, event);
7401 	if (event.doit) {
7402 		StyledTextEvent styledTextEvent = null;
7403 		int replacedLength = event.end - event.start;
7404 		if (isListening(ST.ExtendedModify)) {
7405 			styledTextEvent = new StyledTextEvent(content);
7406 			styledTextEvent.start = event.start;
7407 			styledTextEvent.end = event.start + event.text.length();
7408 			styledTextEvent.text = content.getTextRange(event.start, replacedLength);
7409 		}
7410 		if (updateCaret) {
7411 			//Fix advancing flag for delete/backspace key on direction boundary
7412 			if (event.text.length() == 0) {
7413 				int lineIndex = content.getLineAtOffset(event.start);
7414 				int lineOffset = content.getOffsetAtLine(lineIndex);
7415 				TextLayout layout = renderer.getTextLayout(lineIndex);
7416 				int levelStart = layout.getLevel(event.start - lineOffset);
7417 				int lineIndexEnd = content.getLineAtOffset(event.end);
7418 				if (lineIndex != lineIndexEnd) {
7419 					renderer.disposeTextLayout(layout);
7420 					lineOffset = content.getOffsetAtLine(lineIndexEnd);
7421 					layout = renderer.getTextLayout(lineIndexEnd);
7422 				}
7423 				int levelEnd = layout.getLevel(event.end - lineOffset);
7424 				renderer.disposeTextLayout(layout);
7425 				if (levelStart != levelEnd) {
7426 					caretAlignment = PREVIOUS_OFFSET_TRAILING;
7427 				} else {
7428 					caretAlignment = OFFSET_LEADING;
7429 				}
7430 			}
7431 		}
7432 		content.replaceTextRange(event.start, replacedLength, event.text);
7433 		// set the caret position prior to sending the modify event.
7434 		// fixes 1GBB8NJ
7435 		if (updateCaret && !(blockSelection && blockXLocation != -1)) {
7436 			// always update the caret location. fixes 1G8FODP
7437 			setSelection(event.start + event.text.length(), 0, true, false);
7438 			showCaret();
7439 		}
7440 		notifyListeners(SWT.Modify, event);
7441 		if (isListening(ST.ExtendedModify)) {
7442 			notifyListeners(ST.ExtendedModify, styledTextEvent);
7443 		}
7444 	}
7445 }
paintObject(GC gc, int x, int y, int ascent, int descent, StyleRange style, Bullet bullet, int bulletIndex)7446 void paintObject(GC gc, int x, int y, int ascent, int descent, StyleRange style, Bullet bullet, int bulletIndex) {
7447 	if (isListening(ST.PaintObject)) {
7448 		StyledTextEvent event = new StyledTextEvent (content) ;
7449 		event.gc = gc;
7450 		event.x = x;
7451 		event.y = y;
7452 		event.ascent = ascent;
7453 		event.descent = descent;
7454 		event.style = style;
7455 		event.bullet = bullet;
7456 		event.bulletIndex = bulletIndex;
7457 		notifyListeners(ST.PaintObject, event);
7458 	}
7459 }
7460 /**
7461  * Replaces the selection with the text on the <code>DND.CLIPBOARD</code>
7462  * clipboard  or, if there is no selection,  inserts the text at the current
7463  * caret offset.   If the widget has the SWT.SINGLE style and the
7464  * clipboard text contains more than one line, only the first line without
7465  * line delimiters is  inserted in the widget.
7466  *
7467  * @exception SWTException <ul>
7468  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7469  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7470  * </ul>
7471  */
paste()7472 public void paste(){
7473 	checkWidget();
7474 	String text = (String) getClipboardContent(DND.CLIPBOARD);
7475 	if (text != null && text.length() > 0) {
7476 		if (blockSelection) {
7477 			boolean fillWithSpaces = isFixedLineHeight() && renderer.fixedPitch;
7478 			int offset = insertBlockSelectionText(text, fillWithSpaces);
7479 			setCaretOffset(offset, SWT.DEFAULT);
7480 			clearBlockSelection(true, true);
7481 			setCaretLocation();
7482 			return;
7483 		}
7484 		Event event = new Event();
7485 		event.start = selection.x;
7486 		event.end = selection.y;
7487 		String delimitedText = getModelDelimitedText(text);
7488 		if (textLimit > 0) {
7489 			int uneditedTextLength = getCharCount() - (selection.y - selection.x);
7490 			if ((uneditedTextLength + delimitedText.length()) > textLimit) {
7491 				int endIndex = textLimit - uneditedTextLength;
7492 				delimitedText = delimitedText.substring(0, Math.max(endIndex, 0));
7493 			}
7494 		}
7495 		event.text = delimitedText;
7496 		sendKeyEvent(event);
7497 	}
7498 }
pasteOnMiddleClick(Event event)7499 private void pasteOnMiddleClick(Event event) {
7500 	String text = (String)getClipboardContent(DND.SELECTION_CLIPBOARD);
7501 	if (text != null && text.length() > 0) {
7502 		// position cursor
7503 		doMouseLocationChange(event.x, event.y, false);
7504 		// insert text
7505 		Event e = new Event();
7506 		e.start = selection.x;
7507 		e.end = selection.y;
7508 		e.text = getModelDelimitedText(text);
7509 		sendKeyEvent(e);
7510 	}
7511 }
7512 /**
7513  * Prints the widget's text to the default printer.
7514  *
7515  * @exception SWTException <ul>
7516  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7517  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7518  * </ul>
7519  */
print()7520 public void print() {
7521 	checkWidget();
7522 	Printer printer = new Printer();
7523 	StyledTextPrintOptions options = new StyledTextPrintOptions();
7524 	options.printTextForeground = true;
7525 	options.printTextBackground = true;
7526 	options.printTextFontStyle = true;
7527 	options.printLineBackground = true;
7528 	new Printing(this, printer, options).run();
7529 	printer.dispose();
7530 }
7531 /**
7532  * Returns a runnable that will print the widget's text
7533  * to the specified printer.
7534  * <p>
7535  * The runnable may be run in a non-UI thread.
7536  * </p>
7537  *
7538  * @param printer the printer to print to
7539  *
7540  * @return a <code>Runnable</code> for printing the receiver's text
7541  *
7542  * @exception SWTException <ul>
7543  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7544  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7545  * </ul>
7546  * @exception IllegalArgumentException <ul>
7547  *    <li>ERROR_NULL_ARGUMENT when printer is null</li>
7548  * </ul>
7549  */
print(Printer printer)7550 public Runnable print(Printer printer) {
7551 	checkWidget();
7552 	if (printer == null) {
7553 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
7554 	}
7555 	StyledTextPrintOptions options = new StyledTextPrintOptions();
7556 	options.printTextForeground = true;
7557 	options.printTextBackground = true;
7558 	options.printTextFontStyle = true;
7559 	options.printLineBackground = true;
7560 	return print(printer, options);
7561 }
7562 /**
7563  * Returns a runnable that will print the widget's text
7564  * to the specified printer.
7565  * <p>
7566  * The runnable may be run in a non-UI thread.
7567  * </p>
7568  *
7569  * @param printer the printer to print to
7570  * @param options print options to use during printing
7571  *
7572  * @return a <code>Runnable</code> for printing the receiver's text
7573  *
7574  * @exception SWTException <ul>
7575  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7576  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7577  * </ul>
7578  * @exception IllegalArgumentException <ul>
7579  *    <li>ERROR_NULL_ARGUMENT when printer or options is null</li>
7580  * </ul>
7581  * @since 2.1
7582  */
print(Printer printer, StyledTextPrintOptions options)7583 public Runnable print(Printer printer, StyledTextPrintOptions options) {
7584 	checkWidget();
7585 	if (printer == null || options == null) {
7586 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
7587 	}
7588 	return new Printing(this, printer, options);
7589 }
7590 /**
7591  * Causes the entire bounds of the receiver to be marked
7592  * as needing to be redrawn. The next time a paint request
7593  * is processed, the control will be completely painted.
7594  * <p>
7595  * Recalculates the content width for all lines in the bounds.
7596  * When a <code>LineStyleListener</code> is used a redraw call
7597  * is the only notification to the widget that styles have changed
7598  * and that the content width may have changed.
7599  * </p>
7600  *
7601  * @exception SWTException <ul>
7602  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7603  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7604  * </ul>
7605  *
7606  * @see Control#update()
7607  */
7608 @Override
redraw()7609 public void redraw() {
7610 	super.redraw();
7611 	int itemCount = getPartialBottomIndex() - topIndex + 1;
7612 	renderer.reset(topIndex, itemCount);
7613 	renderer.calculate(topIndex, itemCount);
7614 	setScrollBars(false);
7615 	doMouseLinkCursor();
7616 }
7617 /**
7618  * Causes the rectangular area of the receiver specified by
7619  * the arguments to be marked as needing to be redrawn.
7620  * The next time a paint request is processed, that area of
7621  * the receiver will be painted. If the <code>all</code> flag
7622  * is <code>true</code>, any children of the receiver which
7623  * intersect with the specified area will also paint their
7624  * intersecting areas. If the <code>all</code> flag is
7625  * <code>false</code>, the children will not be painted.
7626  * <p>
7627  * Marks the content width of all lines in the specified rectangle
7628  * as unknown. Recalculates the content width of all visible lines.
7629  * When a <code>LineStyleListener</code> is used a redraw call
7630  * is the only notification to the widget that styles have changed
7631  * and that the content width may have changed.
7632  * </p>
7633  *
7634  * @param x the x coordinate of the area to draw
7635  * @param y the y coordinate of the area to draw
7636  * @param width the width of the area to draw
7637  * @param height the height of the area to draw
7638  * @param all <code>true</code> if children should redraw, and <code>false</code> otherwise
7639  *
7640  * @exception SWTException <ul>
7641  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7642  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7643  * </ul>
7644  *
7645  * @see Control#update()
7646  */
7647 @Override
redraw(int x, int y, int width, int height, boolean all)7648 public void redraw(int x, int y, int width, int height, boolean all) {
7649 	super.redraw(x, y, width, height, all);
7650 	if (height > 0) {
7651 		int firstLine = getLineIndex(y);
7652 		int lastLine = getLineIndex(y + height);
7653 		resetCache(firstLine, lastLine - firstLine + 1);
7654 		doMouseLinkCursor();
7655 	}
7656 }
redrawLines(int startLine, int lineCount, boolean bottomChanged)7657 void redrawLines(int startLine, int lineCount, boolean bottomChanged) {
7658 	// do nothing if redraw range is completely invisible
7659 	int endLine = startLine + lineCount - 1;
7660 	int partialBottomIndex = getPartialBottomIndex();
7661 	int partialTopIndex = getPartialTopIndex();
7662 	if (startLine > partialBottomIndex || endLine < partialTopIndex) {
7663 		return;
7664 	}
7665 	// only redraw visible lines
7666 	if (startLine < partialTopIndex) {
7667 		startLine = partialTopIndex;
7668 	}
7669 	if (endLine > partialBottomIndex) {
7670 		endLine = partialBottomIndex;
7671 	}
7672 	int redrawTop = getLinePixel(startLine);
7673 	int redrawBottom = getLinePixel(endLine + 1);
7674 	if (bottomChanged) redrawBottom = clientAreaHeight - bottomMargin;
7675 	int redrawWidth = clientAreaWidth - leftMargin - rightMargin;
7676 	super.redraw(leftMargin, redrawTop, redrawWidth, redrawBottom - redrawTop, true);
7677 }
redrawLinesBullet(int[] redrawLines)7678 void redrawLinesBullet (int[] redrawLines) {
7679 	if (redrawLines == null) return;
7680 	int topIndex = getPartialTopIndex();
7681 	int bottomIndex = getPartialBottomIndex();
7682 	for (int redrawLine : redrawLines) {
7683 		int lineIndex = redrawLine;
7684 		if (!(topIndex <= lineIndex && lineIndex <= bottomIndex)) continue;
7685 		int width = -1;
7686 		Bullet bullet = renderer.getLineBullet(lineIndex, null);
7687 		if (bullet != null) {
7688 			StyleRange style = bullet.style;
7689 			GlyphMetrics metrics = style.metrics;
7690 			width = metrics.width;
7691 		}
7692 		if (width == -1) width = getClientArea().width;
7693 		int height = renderer.getLineHeight(lineIndex);
7694 		int y = getLinePixel(lineIndex);
7695 		super.redraw(0, y, width, height, false);
7696 	}
7697 }
redrawMargins(int oldHeight, int oldWidth)7698 void redrawMargins(int oldHeight, int oldWidth) {
7699 	/* Redraw the old or new right/bottom margin if needed */
7700 	if (oldWidth != clientAreaWidth) {
7701 		if (rightMargin > 0) {
7702 			int x = (oldWidth < clientAreaWidth ? oldWidth : clientAreaWidth) - rightMargin;
7703 			super.redraw(x, 0, rightMargin, oldHeight, false);
7704 		}
7705 	}
7706 	if (oldHeight != clientAreaHeight) {
7707 		if (bottomMargin > 0) {
7708 			int y = (oldHeight < clientAreaHeight ? oldHeight : clientAreaHeight) - bottomMargin;
7709 			super.redraw(0, y, oldWidth, bottomMargin, false);
7710 		}
7711 	}
7712 }
7713 /**
7714  * Redraws the specified text range.
7715  *
7716  * @param start offset of the first character to redraw
7717  * @param length number of characters to redraw
7718  * @param clearBackground true if the background should be cleared as
7719  *  part of the redraw operation.  If true, the entire redraw range will
7720  *  be cleared before anything is redrawn.  If the redraw range includes
7721  *	the last character of a line (i.e., the entire line is redrawn) the
7722  * 	line is cleared all the way to the right border of the widget.
7723  * 	The redraw operation will be faster and smoother if clearBackground
7724  * 	is set to false.  Whether or not the flag can be set to false depends
7725  * 	on the type of change that has taken place.  If font styles or
7726  * 	background colors for the redraw range have changed, clearBackground
7727  * 	should be set to true.  If only foreground colors have changed for
7728  * 	the redraw range, clearBackground can be set to false.
7729  * @exception SWTException <ul>
7730  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7731  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7732  * </ul>
7733  * @exception IllegalArgumentException <ul>
7734  *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
7735  * </ul>
7736  */
redrawRange(int start, int length, boolean clearBackground)7737 public void redrawRange(int start, int length, boolean clearBackground) {
7738 	checkWidget();
7739 	int end = start + length;
7740 	int contentLength = content.getCharCount();
7741 	if (start > end || start < 0 || end > contentLength) {
7742 		SWT.error(SWT.ERROR_INVALID_RANGE);
7743 	}
7744 	int firstLine = content.getLineAtOffset(start);
7745 	int lastLine = content.getLineAtOffset(end);
7746 	resetCache(firstLine, lastLine - firstLine + 1);
7747 	internalRedrawRange(start, length);
7748 	doMouseLinkCursor();
7749 }
7750 /**
7751  * Removes the specified bidirectional segment listener.
7752  *
7753  * @param listener the listener which should no longer be notified
7754  *
7755  * @exception SWTException <ul>
7756  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7757  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7758  * </ul>
7759  * @exception IllegalArgumentException <ul>
7760  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7761  * </ul>
7762  *
7763  * @since 2.0
7764  */
removeBidiSegmentListener(BidiSegmentListener listener)7765 public void removeBidiSegmentListener(BidiSegmentListener listener) {
7766 	checkWidget();
7767 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7768 	removeListener(ST.LineGetSegments, listener);
7769 	resetCache(0, content.getLineCount());
7770 	setCaretLocation();
7771 	super.redraw();
7772 }
7773 /**
7774  * Removes the specified caret listener.
7775  *
7776  * @param listener the listener which should no longer be notified
7777  *
7778  * @exception SWTException <ul>
7779  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7780  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7781  * </ul>
7782  * @exception IllegalArgumentException <ul>
7783  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7784  * </ul>
7785  *
7786  * @since 3.5
7787  */
removeCaretListener(CaretListener listener)7788 public void removeCaretListener(CaretListener listener) {
7789 	checkWidget();
7790 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7791 	removeListener(ST.CaretMoved, listener);
7792 }
7793 /**
7794  * Removes the specified extended modify listener.
7795  *
7796  * @param extendedModifyListener the listener which should no longer be notified
7797  *
7798  * @exception SWTException <ul>
7799  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7800  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7801  * </ul>
7802  * @exception IllegalArgumentException <ul>
7803  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7804  * </ul>
7805  */
removeExtendedModifyListener(ExtendedModifyListener extendedModifyListener)7806 public void removeExtendedModifyListener(ExtendedModifyListener extendedModifyListener) {
7807 	checkWidget();
7808 	if (extendedModifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7809 	removeListener(ST.ExtendedModify, extendedModifyListener);
7810 }
7811 /**
7812  * Removes the specified line background listener.
7813  *
7814  * @param listener the listener which should no longer be notified
7815  *
7816  * @exception SWTException <ul>
7817  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7818  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7819  * </ul>
7820  * @exception IllegalArgumentException <ul>
7821  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7822  * </ul>
7823  */
removeLineBackgroundListener(LineBackgroundListener listener)7824 public void removeLineBackgroundListener(LineBackgroundListener listener) {
7825 	checkWidget();
7826 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7827 	removeListener(ST.LineGetBackground, listener);
7828 }
7829 /**
7830  * Removes the specified line style listener.
7831  *
7832  * @param listener the listener which should no longer be notified
7833  *
7834  * @exception SWTException <ul>
7835  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7836  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7837  * </ul>
7838  * @exception IllegalArgumentException <ul>
7839  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7840  * </ul>
7841  */
removeLineStyleListener(LineStyleListener listener)7842 public void removeLineStyleListener(LineStyleListener listener) {
7843 	checkWidget();
7844 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7845 	removeListener(ST.LineGetStyle, listener);
7846 	setCaretLocation();
7847 }
7848 /**
7849  * Removes the specified modify listener.
7850  *
7851  * @param modifyListener the listener which should no longer be notified
7852  *
7853  * @exception SWTException <ul>
7854  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7855  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7856  * </ul>
7857  * @exception IllegalArgumentException <ul>
7858  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7859  * </ul>
7860  */
removeModifyListener(ModifyListener modifyListener)7861 public void removeModifyListener(ModifyListener modifyListener) {
7862 	checkWidget();
7863 	if (modifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7864 	removeListener(SWT.Modify, modifyListener);
7865 }
7866 /**
7867  * Removes the specified listener.
7868  *
7869  * @param listener the listener which should no longer be notified
7870  *
7871  * @exception SWTException <ul>
7872  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7873  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7874  * </ul>
7875  * @exception IllegalArgumentException <ul>
7876  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7877  * </ul>
7878  * @since 3.2
7879  */
removePaintObjectListener(PaintObjectListener listener)7880 public void removePaintObjectListener(PaintObjectListener listener) {
7881 	checkWidget();
7882 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7883 	removeListener(ST.PaintObject, listener);
7884 }
7885 /**
7886  * Removes the listener from the collection of listeners who will
7887  * be notified when the user changes the receiver's selection.
7888  *
7889  * @param listener the listener which should no longer be notified
7890  *
7891  * @exception IllegalArgumentException <ul>
7892  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
7893  * </ul>
7894  * @exception SWTException <ul>
7895  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7896  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7897  * </ul>
7898  *
7899  * @see SelectionListener
7900  * @see #addSelectionListener
7901  */
removeSelectionListener(SelectionListener listener)7902 public void removeSelectionListener(SelectionListener listener) {
7903 	checkWidget();
7904 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7905 	removeListener(SWT.Selection, listener);
7906 }
7907 /**
7908  * Removes the specified verify listener.
7909  *
7910  * @param verifyListener the listener which should no longer be notified
7911  *
7912  * @exception SWTException <ul>
7913  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7914  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7915  * </ul>
7916  * @exception IllegalArgumentException <ul>
7917  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7918  * </ul>
7919  */
removeVerifyListener(VerifyListener verifyListener)7920 public void removeVerifyListener(VerifyListener verifyListener) {
7921 	checkWidget();
7922 	if (verifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7923 	removeListener(SWT.Verify, verifyListener);
7924 }
7925 /**
7926  * Removes the specified key verify listener.
7927  *
7928  * @param listener the listener which should no longer be notified
7929  *
7930  * @exception SWTException <ul>
7931  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7932  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7933  * </ul>
7934  * @exception IllegalArgumentException <ul>
7935  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7936  * </ul>
7937  */
removeVerifyKeyListener(VerifyKeyListener listener)7938 public void removeVerifyKeyListener(VerifyKeyListener listener) {
7939 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7940 	removeListener(ST.VerifyKey, listener);
7941 }
7942 /**
7943  * Removes the specified word movement listener.
7944  *
7945  * @param listener the listener which should no longer be notified
7946  *
7947  * @exception SWTException <ul>
7948  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7949  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7950  * </ul>
7951  * @exception IllegalArgumentException <ul>
7952  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
7953  * </ul>
7954  *
7955  * @see MovementEvent
7956  * @see MovementListener
7957  * @see #addWordMovementListener
7958  *
7959  * @since 3.3
7960  */
7961 
removeWordMovementListener(MovementListener listener)7962 public void removeWordMovementListener(MovementListener listener) {
7963 	checkWidget();
7964 	if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
7965 	removeListener(ST.WordNext, listener);
7966 	removeListener(ST.WordPrevious, listener);
7967 }
7968 /**
7969  * Replaces the styles in the given range with new styles.  This method
7970  * effectively deletes the styles in the given range and then adds the
7971  * the new styles.
7972  * <p>
7973  * Note: Because a StyleRange includes the start and length, the
7974  * same instance cannot occur multiple times in the array of styles.
7975  * If the same style attributes, such as font and color, occur in
7976  * multiple StyleRanges, <code>setStyleRanges(int, int, int[], StyleRange[])</code>
7977  * can be used to share styles and reduce memory usage.
7978  * </p><p>
7979  * Should not be called if a LineStyleListener has been set since the
7980  * listener maintains the styles.
7981  * </p>
7982  *
7983  * @param start offset of first character where styles will be deleted
7984  * @param length length of the range to delete styles in
7985  * @param ranges StyleRange objects containing the new style information.
7986  * The ranges should not overlap and should be within the specified start
7987  * and length. The style rendering is undefined if the ranges do overlap
7988  * or are ill-defined. Must not be null.
7989  * @exception SWTException <ul>
7990  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
7991  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
7992  * </ul>
7993  * @exception IllegalArgumentException <ul>
7994  *   <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 &lt;= offset &lt;= getCharCount())</li>
7995  *   <li>ERROR_NULL_ARGUMENT when ranges is null</li>
7996  * </ul>
7997  *
7998  * @since 2.0
7999  *
8000  * @see #setStyleRanges(int, int, int[], StyleRange[])
8001  */
replaceStyleRanges(int start, int length, StyleRange[] ranges)8002 public void replaceStyleRanges(int start, int length, StyleRange[] ranges) {
8003 	checkWidget();
8004 	if (isListening(ST.LineGetStyle)) return;
8005 	if (ranges == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
8006 	setStyleRanges(start, length, null, ranges, false);
8007 }
8008 /**
8009  * Replaces the given text range with new text.
8010  * If the widget has the SWT.SINGLE style and "text" contains more than
8011  * one line, only the first line is rendered but the text is stored
8012  * unchanged. A subsequent call to getText will return the same text
8013  * that was set. Note that only a single line of text should be set when
8014  * the SWT.SINGLE style is used.
8015  * <p>
8016  * <b>NOTE:</b> During the replace operation the current selection is
8017  * changed as follows:
8018  * </p>
8019  * <ul>
8020  * <li>selection before replaced text: selection unchanged
8021  * <li>selection after replaced text: adjust the selection so that same text
8022  * remains selected
8023  * <li>selection intersects replaced text: selection is cleared and caret
8024  * is placed after inserted text
8025  * </ul>
8026  *
8027  * @param start offset of first character to replace
8028  * @param length number of characters to replace. Use 0 to insert text
8029  * @param text new text. May be empty to delete text.
8030  * @exception SWTException <ul>
8031  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8032  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8033  * </ul>
8034  * @exception IllegalArgumentException <ul>
8035  *   <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 &lt;= offset &lt;= getCharCount())</li>
8036  *   <li>ERROR_INVALID_ARGUMENT when either start or end is inside a multi byte line delimiter.
8037  * 		Splitting a line delimiter for example by inserting text in between the CR and LF and deleting part of a line delimiter is not supported</li>
8038  *   <li>ERROR_NULL_ARGUMENT when string is null</li>
8039  * </ul>
8040  */
replaceTextRange(int start, int length, String text)8041 public void replaceTextRange(int start, int length, String text) {
8042 	checkWidget();
8043 	if (text == null) {
8044 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
8045 	}
8046 	int contentLength = getCharCount();
8047 	int end = start + length;
8048 	if (start > end || start < 0 || end > contentLength) {
8049 		SWT.error(SWT.ERROR_INVALID_RANGE);
8050 	}
8051 	Event event = new Event();
8052 	event.start = start;
8053 	event.end = end;
8054 	event.text = text;
8055 	modifyContent(event, false);
8056 }
8057 /**
8058  * Resets the caret position, selection and scroll offsets. Recalculate
8059  * the content width and scroll bars. Redraw the widget.
8060  */
reset()8061 void reset() {
8062 	ScrollBar verticalBar = getVerticalBar();
8063 	ScrollBar horizontalBar = getHorizontalBar();
8064 	setCaretOffset(0, SWT.DEFAULT);
8065 	topIndex = 0;
8066 	topIndexY = 0;
8067 	verticalScrollOffset = 0;
8068 	horizontalScrollOffset = 0;
8069 	resetSelection();
8070 	renderer.setContent(content);
8071 	if (verticalBar != null) {
8072 		verticalBar.setSelection(0);
8073 	}
8074 	if (horizontalBar != null) {
8075 		horizontalBar.setSelection(0);
8076 	}
8077 	resetCache(0, 0);
8078 	setCaretLocation();
8079 	super.redraw();
8080 }
resetBidiData()8081 void resetBidiData() {
8082 	caretDirection = SWT.NULL;
8083 	resetCache(0, content.getLineCount());
8084 	setCaretLocation();
8085 	keyActionMap.clear();
8086 	createKeyBindings();
8087 	super.redraw();
8088 }
resetCache(SortedSet<Integer> lines)8089 void resetCache(SortedSet<Integer> lines) {
8090 	if (lines == null || lines.isEmpty()) return;
8091 	int maxLineIndex = renderer.maxWidthLineIndex;
8092 	renderer.reset(lines);
8093 	renderer.calculateClientArea();
8094 	if (0 <= maxLineIndex && maxLineIndex < content.getLineCount()) {
8095 		renderer.calculate(maxLineIndex, 1);
8096 	}
8097 	setScrollBars(true);
8098 	if (!isFixedLineHeight()) {
8099 		if (topIndex > lines.iterator().next()) {
8100 			verticalScrollOffset = -1;
8101 		}
8102 		renderer.calculateIdle();
8103 	}
8104 }
resetCache(int firstLine, int count)8105 void resetCache(int firstLine, int count) {
8106 	int maxLineIndex = renderer.maxWidthLineIndex;
8107 	renderer.reset(firstLine, count);
8108 	renderer.calculateClientArea();
8109 	if (0 <= maxLineIndex && maxLineIndex < content.getLineCount()) {
8110 		renderer.calculate(maxLineIndex, 1);
8111 	}
8112 	setScrollBars(true);
8113 	if (!isFixedLineHeight()) {
8114 		if (topIndex > firstLine) {
8115 			verticalScrollOffset = -1;
8116 		}
8117 		renderer.calculateIdle();
8118 	}
8119 }
8120 /**
8121  * Resets the selection.
8122  */
resetSelection()8123 void resetSelection() {
8124 	selection.x = selection.y = caretOffset;
8125 	selectionAnchor = -1;
8126 	sendAccessibleTextCaretMoved();
8127 }
8128 
8129 @Override
scroll(int destX, int destY, int x, int y, int width, int height, boolean all)8130 public void scroll(int destX, int destY, int x, int y, int width, int height, boolean all) {
8131 	super.scroll(destX, destY, x, y, width, height, false);
8132 	if (all) {
8133 		int deltaX = destX - x, deltaY = destY - y;
8134 		for (Control child : getChildren()) {
8135 			Rectangle rect = child.getBounds();
8136 			child.setLocation(rect.x + deltaX, rect.y + deltaY);
8137 		}
8138 	}
8139 }
8140 
8141 /**
8142  * Scrolls the widget horizontally.
8143  *
8144  * @param pixels number of SWT logical points to scroll, &gt; 0 = scroll left,
8145  * 	&lt; 0 scroll right
8146  * @param adjustScrollBar
8147  * 	true= the scroll thumb will be moved to reflect the new scroll offset.
8148  * 	false = the scroll thumb will not be moved
8149  * @return
8150  *	true=the widget was scrolled
8151  *	false=the widget was not scrolled, the given offset is not valid.
8152  */
scrollHorizontal(int pixels, boolean adjustScrollBar)8153 boolean scrollHorizontal(int pixels, boolean adjustScrollBar) {
8154 	if (pixels == 0) return false;
8155 	if (wordWrap) return false;
8156 	ScrollBar horizontalBar = getHorizontalBar();
8157 	if (horizontalBar != null && adjustScrollBar) {
8158 		horizontalBar.setSelection(horizontalScrollOffset + pixels);
8159 	}
8160 	int scrollHeight = clientAreaHeight - topMargin - bottomMargin;
8161 	if (pixels > 0) {
8162 		int sourceX = leftMargin + pixels;
8163 		int scrollWidth = clientAreaWidth - sourceX - rightMargin;
8164 		if (scrollWidth > 0) {
8165 			scroll(leftMargin, topMargin, sourceX, topMargin, scrollWidth, scrollHeight, true);
8166 		}
8167 		if (sourceX > scrollWidth) {
8168 			super.redraw(leftMargin + scrollWidth, topMargin, pixels - scrollWidth, scrollHeight, true);
8169 		}
8170 	} else {
8171 		int destinationX = leftMargin - pixels;
8172 		int scrollWidth = clientAreaWidth - destinationX - rightMargin;
8173 		if (scrollWidth > 0) {
8174 			scroll(destinationX, topMargin, leftMargin, topMargin, scrollWidth, scrollHeight, true);
8175 		}
8176 		if (destinationX > scrollWidth) {
8177 			super.redraw(leftMargin + scrollWidth, topMargin, -pixels - scrollWidth, scrollHeight, true);
8178 		}
8179 	}
8180 	horizontalScrollOffset += pixels;
8181 	setCaretLocation();
8182 	return true;
8183 }
8184 /**
8185  * Scrolls the widget vertically.
8186  *
8187  * @param pixel the new vertical scroll offset
8188  * @param adjustScrollBar
8189  * 	true= the scroll thumb will be moved to reflect the new scroll offset.
8190  * 	false = the scroll thumb will not be moved
8191  * @return
8192  *	true=the widget was scrolled
8193  *	false=the widget was not scrolled
8194  */
scrollVertical(int pixels, boolean adjustScrollBar)8195 boolean scrollVertical(int pixels, boolean adjustScrollBar) {
8196 	if (pixels == 0) {
8197 		return false;
8198 	}
8199 	if (verticalScrollOffset != -1) {
8200 		ScrollBar verticalBar = getVerticalBar();
8201 		if (verticalBar != null && adjustScrollBar) {
8202 			verticalBar.setSelection(verticalScrollOffset + pixels);
8203 		}
8204 		int deltaY = 0;
8205 		if (pixels > 0) {
8206 			int sourceY = topMargin + pixels;
8207 			int scrollHeight = clientAreaHeight - sourceY - bottomMargin;
8208 			if (scrollHeight > 0) {
8209 				deltaY = -pixels;
8210 			}
8211 		} else {
8212 			int destinationY = topMargin - pixels;
8213 			int scrollHeight = clientAreaHeight - destinationY - bottomMargin;
8214 			if (scrollHeight > 0) {
8215 				deltaY = -pixels;
8216 			}
8217 		}
8218 		Control[] children = getChildren();
8219 		for (Control child : children) {
8220 			Rectangle rect = child.getBounds();
8221 			child.setLocation(rect.x, rect.y + deltaY);
8222 		}
8223 		verticalScrollOffset += pixels;
8224 		calculateTopIndex(pixels);
8225 		super.redraw();
8226 	} else {
8227 		calculateTopIndex(pixels);
8228 		super.redraw();
8229 	}
8230 	setCaretLocation();
8231 	return true;
8232 }
scrollText(int srcY, int destY)8233 void scrollText(int srcY, int destY) {
8234 	if (srcY == destY) return;
8235 	int deltaY = destY - srcY;
8236 	int scrollWidth = clientAreaWidth - leftMargin - rightMargin, scrollHeight;
8237 	if (deltaY > 0) {
8238 		scrollHeight = clientAreaHeight - srcY - bottomMargin;
8239 	} else {
8240 		scrollHeight = clientAreaHeight - destY - bottomMargin;
8241 	}
8242 	scroll(leftMargin, destY, leftMargin, srcY, scrollWidth, scrollHeight, true);
8243 	if ((0 < srcY + scrollHeight) && (topMargin > srcY)) {
8244 		super.redraw(leftMargin, deltaY, scrollWidth, topMargin, false);
8245 	}
8246 	if ((0 < destY + scrollHeight) && (topMargin > destY)) {
8247 		super.redraw(leftMargin, 0, scrollWidth, topMargin, false);
8248 	}
8249 	if ((clientAreaHeight - bottomMargin < srcY + scrollHeight) && (clientAreaHeight > srcY)) {
8250 		super.redraw(leftMargin, clientAreaHeight - bottomMargin + deltaY, scrollWidth, bottomMargin, false);
8251 	}
8252 	if ((clientAreaHeight - bottomMargin < destY + scrollHeight) && (clientAreaHeight > destY)) {
8253 		super.redraw(leftMargin, clientAreaHeight - bottomMargin, scrollWidth, bottomMargin, false);
8254 	}
8255 }
sendAccessibleTextCaretMoved()8256 void sendAccessibleTextCaretMoved() {
8257 	if (caretOffset != accCaretOffset) {
8258 		accCaretOffset = caretOffset;
8259 		getAccessible().textCaretMoved(caretOffset);
8260 	}
8261 }
sendAccessibleTextChanged(int start, int newCharCount, int replaceCharCount)8262 void sendAccessibleTextChanged(int start, int newCharCount, int replaceCharCount) {
8263 	Accessible accessible = getAccessible();
8264 	if (replaceCharCount != 0) {
8265 		accessible.textChanged(ACC.TEXT_DELETE, start, replaceCharCount);
8266 	}
8267 	if (newCharCount != 0) {
8268 		accessible.textChanged(ACC.TEXT_INSERT, start, newCharCount);
8269 	}
8270 }
8271 /**
8272  * Selects all the text.
8273  *
8274  * @exception SWTException <ul>
8275  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8276  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8277  * </ul>
8278  */
selectAll()8279 public void selectAll() {
8280 	checkWidget();
8281 	if (blockSelection) {
8282 		renderer.calculate(0, content.getLineCount());
8283 		setScrollBars(false);
8284 		int verticalScrollOffset = getVerticalScrollOffset();
8285 		int left = leftMargin - horizontalScrollOffset;
8286 		int top = topMargin - verticalScrollOffset;
8287 		int right = renderer.getWidth() - rightMargin - horizontalScrollOffset;
8288 		int bottom = renderer.getHeight() - bottomMargin - verticalScrollOffset;
8289 		setBlockSelectionLocation(left, top, right, bottom, false);
8290 		return;
8291 	}
8292 	setSelection(0, Math.max(getCharCount(),0));
8293 }
8294 /**
8295  * Replaces/inserts text as defined by the event.
8296  *
8297  * @param event the text change event.
8298  *	<ul>
8299  *	<li>event.start - the replace start offset</li>
8300  * 	<li>event.end - the replace end offset</li>
8301  * 	<li>event.text - the new text</li>
8302  *	</ul>
8303  */
sendKeyEvent(Event event)8304 void sendKeyEvent(Event event) {
8305 	if (editable) {
8306 		modifyContent(event, true);
8307 	}
8308 }
8309 /**
8310  * Returns a StyledTextEvent that can be used to request data such
8311  * as styles and background color for a line.
8312  * <p>
8313  * The specified line may be a visual (wrapped) line if in word
8314  * wrap mode. The returned object will always be for a logical
8315  * (unwrapped) line.
8316  * </p>
8317  *
8318  * @param lineOffset offset of the line. This may be the offset of
8319  * 	a visual line if the widget is in word wrap mode.
8320  * @param line line text. This may be the text of a visual line if
8321  * 	the widget is in word wrap mode.
8322  * @return StyledTextEvent that can be used to request line data
8323  * 	for the given line.
8324  */
sendLineEvent(int eventType, int lineOffset, String line)8325 StyledTextEvent sendLineEvent(int eventType, int lineOffset, String line) {
8326 	StyledTextEvent event = null;
8327 	if (isListening(eventType)) {
8328 		event = new StyledTextEvent(content);
8329 		event.detail = lineOffset;
8330 		event.text = line;
8331 		event.alignment = alignment;
8332 		event.indent = indent;
8333 		event.wrapIndent = wrapIndent;
8334 		event.justify = justify;
8335 		notifyListeners(eventType, event);
8336 	}
8337 	return event;
8338 }
8339 /**
8340  * Sends the specified selection event.
8341  */
sendSelectionEvent()8342 void sendSelectionEvent() {
8343 	getAccessible().textSelectionChanged();
8344 	Event event = new Event();
8345 	event.x = selection.x;
8346 	event.y = selection.y;
8347 	notifyListeners(SWT.Selection, event);
8348 }
sendTextEvent(int left, int right, int lineIndex, String text, boolean fillWithSpaces)8349 int sendTextEvent(int left, int right, int lineIndex, String text, boolean fillWithSpaces) {
8350 	int lineWidth = 0, start, end;
8351 	StringBuilder buffer = new StringBuilder();
8352 	if (lineIndex < content.getLineCount()) {
8353 		int[] trailing = new int[1];
8354 		start = getOffsetAtPoint(left, getLinePixel(lineIndex), trailing, true);
8355 		if (start == -1) {
8356 			int lineOffset = content.getOffsetAtLine(lineIndex);
8357 			int lineLegth = content.getLine(lineIndex).length();
8358 			start = end = lineOffset + lineLegth;
8359 			if (fillWithSpaces) {
8360 				TextLayout layout = renderer.getTextLayout(lineIndex);
8361 				lineWidth = layout.getBounds().width;
8362 				renderer.disposeTextLayout(layout);
8363 			}
8364 		} else {
8365 			start += trailing[0];
8366 			end = left == right ? start : getOffsetAtPoint(right, 0, lineIndex, null);
8367 			fillWithSpaces = false;
8368 		}
8369 	} else {
8370 		start = end = content.getCharCount();
8371 		buffer.append(content.getLineDelimiter());
8372 	}
8373 	if (start > end) {
8374 		int temp = start;
8375 		start = end;
8376 		end = temp;
8377 	}
8378 	if (fillWithSpaces) {
8379 		int spacesWidth = left - lineWidth + horizontalScrollOffset - leftMargin;
8380 		int spacesCount = spacesWidth / renderer.averageCharWidth;
8381 		for (int i = 0; i < spacesCount; i++) {
8382 			buffer.append(' ');
8383 		}
8384 	}
8385 	buffer.append(text);
8386 	Event event = new Event();
8387 	event.start = start;
8388 	event.end = end;
8389 	event.text = buffer.toString();
8390 	sendKeyEvent(event);
8391 	return event.start + event.text.length();
8392 }
sendWordBoundaryEvent(int eventType, int movement, int offset, int newOffset, String lineText, int lineOffset)8393 int sendWordBoundaryEvent(int eventType, int movement, int offset, int newOffset, String lineText, int lineOffset) {
8394 	if (isListening(eventType)) {
8395 		StyledTextEvent event = new StyledTextEvent(content);
8396 		event.detail = lineOffset;
8397 		event.text = lineText;
8398 		event.count = movement;
8399 		event.start = offset;
8400 		event.end = newOffset;
8401 		notifyListeners(eventType, event);
8402 		offset = event.end;
8403 		if (offset != newOffset) {
8404 			int length = getCharCount();
8405 			if (offset < 0) {
8406 				offset = 0;
8407 			} else if (offset > length) {
8408 				offset = length;
8409 			} else {
8410 				if (isLineDelimiter(offset)) {
8411 					SWT.error(SWT.ERROR_INVALID_ARGUMENT);
8412 				}
8413 			}
8414 		}
8415 		return offset;
8416 	}
8417 	return newOffset;
8418 }
setAlignment()8419 void setAlignment() {
8420 	if ((getStyle() & SWT.SINGLE) == 0) return;
8421 	int alignment = renderer.getLineAlignment(0, this.alignment);
8422 	int newAlignmentMargin = 0;
8423 	if (alignment != SWT.LEFT) {
8424 		renderer.calculate(0, 1);
8425 		int width = renderer.getWidth() - alignmentMargin;
8426 		newAlignmentMargin = clientAreaWidth - width;
8427 		if (newAlignmentMargin < 0) newAlignmentMargin = 0;
8428 		if (alignment == SWT.CENTER) newAlignmentMargin /= 2;
8429 	}
8430 	if (alignmentMargin != newAlignmentMargin) {
8431 		leftMargin -= alignmentMargin;
8432 		leftMargin += newAlignmentMargin;
8433 		alignmentMargin = newAlignmentMargin;
8434 		resetCache(0, 1);
8435 		setCaretLocation();
8436 		super.redraw();
8437 	}
8438 }
8439 /**
8440  * Sets the alignment of the widget. The argument should be one of <code>SWT.LEFT</code>,
8441  * <code>SWT.CENTER</code> or <code>SWT.RIGHT</code>. The alignment applies for all lines.
8442  * <p>
8443  * Note that if <code>SWT.MULTI</code> is set, then <code>SWT.WRAP</code> must also be set
8444  * in order to stabilize the right edge before setting alignment.
8445  * </p>
8446  *
8447  * @param alignment the new alignment
8448  *
8449  * @exception SWTException <ul>
8450  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8451  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8452  * </ul>
8453  *
8454  * @see #setLineAlignment(int, int, int)
8455  *
8456  * @since 3.2
8457  */
setAlignment(int alignment)8458 public void setAlignment(int alignment) {
8459 	checkWidget();
8460 	alignment &= (SWT.LEFT | SWT.RIGHT | SWT.CENTER);
8461 	if (alignment == 0 || this.alignment == alignment) return;
8462 	this.alignment = alignment;
8463 	resetCache(0, content.getLineCount());
8464 	setCaretLocation();
8465 	setAlignment();
8466 	super.redraw();
8467 }
8468 /**
8469  * Set the Always Show Scrollbars flag.  True if the scrollbars are
8470  * always shown even if they are not required (default value).  False if the scrollbars are only
8471  * visible when some part of the content needs to be scrolled to be seen.
8472  * The H_SCROLL and V_SCROLL style bits are also required to enable scrollbars in the
8473  * horizontal and vertical directions.
8474  *
8475  * @param show true to show the scrollbars even when not required, false to show scrollbars only when required
8476  *
8477  * @exception SWTException <ul>
8478  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8479  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8480  * </ul>
8481  *
8482  * @since 3.8
8483  */
setAlwaysShowScrollBars(boolean show)8484 public void setAlwaysShowScrollBars(boolean show) {
8485 	checkWidget();
8486 	if (show == alwaysShowScroll) return;
8487 	alwaysShowScroll = show;
8488 	setScrollBars(true);
8489 }
8490 /**
8491  * @see Control#setBackground(Color)
8492  */
8493 @Override
setBackground(Color color)8494 public void setBackground(Color color) {
8495 	checkWidget();
8496 	boolean backgroundDisabled = false;
8497 	if (!this.enabled && color == null) {
8498 		if (background != null) {
8499 			Color disabledBg = getDisplay().getSystemColor(SWT.COLOR_TEXT_DISABLED_BACKGROUND);
8500 			if (background.equals(disabledBg)) {
8501 				return;
8502 			} else {
8503 				color = new Color (getDisplay(), disabledBg.getRGBA());
8504 				backgroundDisabled = true;
8505 			}
8506 		}
8507 	}
8508 	customBackground = color != null && !this.insideSetEnableCall && !backgroundDisabled;
8509 	background = color;
8510 	super.setBackground(color);
8511 	resetCache(0, content.getLineCount());
8512 	setCaretLocation();
8513 	super.redraw();
8514 }
8515 /**
8516  * Sets the block selection mode.
8517  *
8518  * @param blockSelection true=enable block selection, false=disable block selection
8519  *
8520  * @since 3.5
8521  */
setBlockSelection(boolean blockSelection)8522 public void setBlockSelection(boolean blockSelection) {
8523 	checkWidget();
8524 	if ((getStyle() & SWT.SINGLE) != 0) return;
8525 	if (blockSelection == this.blockSelection) return;
8526 	if (wordWrap) return;
8527 	this.blockSelection = blockSelection;
8528 	if (cursor == null) {
8529 		Display display = getDisplay();
8530 		int type = blockSelection ? SWT.CURSOR_CROSS : SWT.CURSOR_IBEAM;
8531 		super.setCursor(display.getSystemCursor(type));
8532 	}
8533 	if (blockSelection) {
8534 		int start = selection.x;
8535 		int end = selection.y;
8536 		if (start != end) {
8537 			setBlockSelectionOffset(start, end, false);
8538 		}
8539 	} else {
8540 		clearBlockSelection(false, false);
8541 	}
8542 }
8543 /**
8544  * Sets the block selection bounds. The bounds is
8545  * relative to the upper left corner of the document.
8546  *
8547  * @param rect the new bounds for the block selection
8548  *
8549  * @see #setBlockSelectionBounds(int, int, int, int)
8550  * @exception SWTException <ul>
8551  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8552  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8553  * </ul>
8554  * @exception IllegalArgumentException <ul>
8555  *   <li>ERROR_NULL_ARGUMENT when point is null</li>
8556  * </ul>
8557  *
8558  * @since 3.5
8559  */
setBlockSelectionBounds(Rectangle rect)8560 public void setBlockSelectionBounds(Rectangle rect) {
8561 	checkWidget();
8562 	if (rect == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
8563 	setBlockSelectionBounds(rect.x, rect.y, rect.width, rect.height);
8564 }
8565 /**
8566  * Sets the block selection bounds. The bounds is
8567  * relative to the upper left corner of the document.
8568  *
8569  * @param x the new x coordinate for the block selection
8570  * @param y the new y coordinate for the block selection
8571  * @param width the new width for the block selection
8572  * @param height the new height for the block selection
8573  *
8574  * @exception SWTException <ul>
8575  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8576  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8577  * </ul>
8578  *
8579  * @since 3.5
8580  */
setBlockSelectionBounds(int x, int y, int width, int height)8581 public void setBlockSelectionBounds(int x, int y, int width, int height) {
8582 	checkWidget();
8583 	int verticalScrollOffset = getVerticalScrollOffset();
8584 	if (!blockSelection) {
8585 		x -= horizontalScrollOffset;
8586 		y -= verticalScrollOffset;
8587 		int start = getOffsetAtPoint(x, y, null);
8588 		int end = getOffsetAtPoint(x+width-1, y+height-1, null);
8589 		setSelection(start, end - start, false, false);
8590 		setCaretLocation();
8591 		return;
8592 	}
8593 	int minY = topMargin;
8594 	int minX = leftMargin;
8595 	int maxY = renderer.getHeight() - bottomMargin;
8596 	int maxX = Math.max(clientAreaWidth, renderer.getWidth()) - rightMargin;
8597 	int anchorX = Math.max(minX, Math.min(maxX, x)) - horizontalScrollOffset;
8598 	int anchorY = Math.max(minY, Math.min(maxY, y)) - verticalScrollOffset;
8599 	int locationX = Math.max(minX, Math.min(maxX, x + width)) - horizontalScrollOffset;
8600 	int locationY = Math.max(minY, Math.min(maxY, y + height - 1)) - verticalScrollOffset;
8601 	if (isFixedLineHeight() && renderer.fixedPitch) {
8602 		int avg = renderer.averageCharWidth;
8603 		anchorX = ((anchorX - leftMargin + horizontalScrollOffset) / avg * avg) + leftMargin - horizontalScrollOffset;
8604 		locationX = ((locationX + avg / 2 - leftMargin + horizontalScrollOffset) / avg * avg) + leftMargin - horizontalScrollOffset;
8605 	}
8606 	setBlockSelectionLocation(anchorX, anchorY, locationX, locationY, false);
8607 }
setBlockSelectionLocation(int x, int y, boolean sendEvent)8608 void setBlockSelectionLocation (int x, int y, boolean sendEvent) {
8609 	int verticalScrollOffset = getVerticalScrollOffset();
8610 	blockXLocation = x + horizontalScrollOffset;
8611 	blockYLocation = y + verticalScrollOffset;
8612 	int[] alignment = new int[1];
8613 	int offset = getOffsetAtPoint(x, y, alignment);
8614 	setCaretOffset(offset, alignment[0]);
8615 	if (blockXAnchor == -1) {
8616 		blockXAnchor = blockXLocation;
8617 		blockYAnchor = blockYLocation;
8618 		selectionAnchor = caretOffset;
8619 	}
8620 	doBlockSelection(sendEvent);
8621 }
setBlockSelectionLocation(int anchorX, int anchorY, int x, int y, boolean sendEvent)8622 void setBlockSelectionLocation (int anchorX, int anchorY, int x, int y, boolean sendEvent) {
8623 	int verticalScrollOffset = getVerticalScrollOffset();
8624 	blockXAnchor = anchorX + horizontalScrollOffset;
8625 	blockYAnchor = anchorY + verticalScrollOffset;
8626 	selectionAnchor = getOffsetAtPoint(anchorX, anchorY, null);
8627 	setBlockSelectionLocation(x, y, sendEvent);
8628 }
setBlockSelectionOffset(int offset, boolean sendEvent)8629 void setBlockSelectionOffset (int offset, boolean sendEvent) {
8630 	Point point = getPointAtOffset(offset);
8631 	int verticalScrollOffset = getVerticalScrollOffset();
8632 	blockXLocation = point.x + horizontalScrollOffset;
8633 	blockYLocation = point.y + verticalScrollOffset;
8634 	setCaretOffset(offset, SWT.DEFAULT);
8635 	if (blockXAnchor == -1) {
8636 		blockXAnchor = blockXLocation;
8637 		blockYAnchor = blockYLocation;
8638 		selectionAnchor = caretOffset;
8639 	}
8640 	doBlockSelection(sendEvent);
8641 }
setBlockSelectionOffset(int anchorOffset, int offset, boolean sendEvent)8642 void setBlockSelectionOffset (int anchorOffset, int offset, boolean sendEvent) {
8643 	int verticalScrollOffset = getVerticalScrollOffset();
8644 	Point anchorPoint = getPointAtOffset(anchorOffset);
8645 	blockXAnchor = anchorPoint.x + horizontalScrollOffset;
8646 	blockYAnchor = anchorPoint.y + verticalScrollOffset;
8647 	selectionAnchor = anchorOffset;
8648 	setBlockSelectionOffset(offset, sendEvent);
8649 }
8650 /**
8651  * Sets the receiver's caret.  Set the caret's height and location.
8652  *
8653  * @param caret the new caret for the receiver
8654  *
8655  * @exception SWTException <ul>
8656  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8657  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8658  * </ul>
8659  */
8660 @Override
setCaret(Caret caret)8661 public void setCaret(Caret caret) {
8662 	checkWidget ();
8663 	super.setCaret(caret);
8664 	caretDirection = SWT.NULL;
8665 	if (caret != null) {
8666 		setCaretLocation();
8667 	}
8668 }
8669 /**
8670  * Sets the BIDI coloring mode.  When true the BIDI text display
8671  * algorithm is applied to segments of text that are the same
8672  * color.
8673  *
8674  * @param mode the new coloring mode
8675  * @exception SWTException <ul>
8676  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8677  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8678  * </ul>
8679  *
8680  * @deprecated use BidiSegmentListener instead.
8681  */
8682 @Deprecated
setBidiColoring(boolean mode)8683 public void setBidiColoring(boolean mode) {
8684 	checkWidget();
8685 	bidiColoring = mode;
8686 }
8687 /**
8688  * Sets the bottom margin.
8689  *
8690  * @param bottomMargin the bottom margin.
8691  * @exception SWTException <ul>
8692  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8693  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8694  * </ul>
8695  *
8696  * @since 3.5
8697  */
setBottomMargin(int bottomMargin)8698 public void setBottomMargin (int bottomMargin) {
8699 	checkWidget();
8700 	setMargins(getLeftMargin(), topMargin, rightMargin, bottomMargin);
8701 }
8702 /**
8703  * Moves the Caret to the current caret offset.
8704  */
setCaretLocation()8705 void setCaretLocation() {
8706 	Point newCaretPos = getPointAtOffset(caretOffset);
8707 	setCaretLocation(newCaretPos, getCaretDirection());
8708 }
setCaretLocation(final Point location, int direction)8709 void setCaretLocation(final Point location, int direction) {
8710 	Caret caret = getCaret();
8711 	if (caret != null) {
8712 		final boolean isDefaultCaret = caret == defaultCaret;
8713 		final StyleRange styleAtOffset = content.getCharCount() > 0 ?
8714 			(caretOffset < content.getCharCount() ?
8715 				getStyleRangeAtOffset(caretOffset) :
8716 				getStyleRangeAtOffset(content.getCharCount() - 1)) : // caret after last char: use last char style
8717 			null;
8718 
8719 		int graphicalLineHeight = getLineHeight(caretOffset);
8720 		int caretHeight = getLineHeight();
8721 
8722 		if (styleAtOffset != null && styleAtOffset.isVariableHeight()) {
8723 			if (isDefaultCaret) {
8724 				direction = SWT.DEFAULT;
8725 				caretHeight = graphicalLineHeight;
8726 			} else {
8727 				caretHeight = caret.getSize().y;
8728 			}
8729 		}
8730 		if (caretHeight < graphicalLineHeight) {
8731 			location.y += (graphicalLineHeight - caretHeight);
8732 		}
8733 
8734 		int imageDirection = direction;
8735 		if (isMirrored()) {
8736 			if (imageDirection == SWT.LEFT) {
8737 				imageDirection = SWT.RIGHT;
8738 			} else if (imageDirection == SWT.RIGHT) {
8739 				imageDirection = SWT.LEFT;
8740 			}
8741 		}
8742 		if (isDefaultCaret && imageDirection == SWT.RIGHT) {
8743 			location.x -= (caret.getSize().x - 1);
8744 		}
8745 		if (isDefaultCaret) {
8746 			caret.setBounds(location.x, location.y, caretWidth, caretHeight);
8747 		} else {
8748 			caret.setLocation(location);
8749 		}
8750 		if (direction != caretDirection) {
8751 			caretDirection = direction;
8752 			if (isDefaultCaret) {
8753 				if (imageDirection == SWT.DEFAULT) {
8754 					defaultCaret.setImage(null);
8755 				} else if (imageDirection == SWT.LEFT) {
8756 					defaultCaret.setImage(leftCaretBitmap);
8757 				} else if (imageDirection == SWT.RIGHT) {
8758 					defaultCaret.setImage(rightCaretBitmap);
8759 				}
8760 			}
8761 			if (caretDirection == SWT.LEFT) {
8762 				BidiUtil.setKeyboardLanguage(BidiUtil.KEYBOARD_NON_BIDI);
8763 			} else if (caretDirection == SWT.RIGHT) {
8764 				BidiUtil.setKeyboardLanguage(BidiUtil.KEYBOARD_BIDI);
8765 			}
8766 		}
8767 		updateCaretVisibility();
8768 	}
8769 	columnX = location.x;
8770 }
8771 /**
8772  * Sets the caret offset.
8773  *
8774  * @param offset caret offset, relative to the first character in the text.
8775  * @exception SWTException <ul>
8776  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8777  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8778  * </ul>
8779  * @exception IllegalArgumentException <ul>
8780  *   <li>ERROR_INVALID_ARGUMENT when the offset is inside a multi byte line
8781  *   delimiter (and thus neither clearly in front of or after the line delimiter)
8782  * </ul>
8783  */
setCaretOffset(int offset)8784 public void setCaretOffset(int offset) {
8785 	checkWidget();
8786 	int length = getCharCount();
8787 	if (length > 0 && offset != caretOffset) {
8788 		if (offset < 0) {
8789 			offset = 0;
8790 		} else if (offset > length) {
8791 			offset = length;
8792 		} else {
8793 			if (isLineDelimiter(offset)) {
8794 				// offset is inside a multi byte line delimiter. This is an
8795 				// illegal operation and an exception is thrown. Fixes 1GDKK3R
8796 				SWT.error(SWT.ERROR_INVALID_ARGUMENT);
8797 			}
8798 		}
8799 		setCaretOffset(offset, PREVIOUS_OFFSET_TRAILING);
8800 		// clear the selection if the caret is moved.
8801 		// don't notify listeners about the selection change.
8802 		if (blockSelection) {
8803 			clearBlockSelection(true, false);
8804 		} else {
8805 			clearSelection(false);
8806 		}
8807 	}
8808 	setCaretLocation();
8809 }
setCaretOffset(int offset, int alignment)8810 void setCaretOffset(int offset, int alignment) {
8811 	if (caretOffset != offset) {
8812 		caretOffset = offset;
8813 		if (isListening(ST.CaretMoved)) {
8814 			StyledTextEvent event = new StyledTextEvent(content);
8815 			event.end = caretOffset;
8816 			notifyListeners(ST.CaretMoved, event);
8817 		}
8818 	}
8819 	if (alignment != SWT.DEFAULT) {
8820 		caretAlignment = alignment;
8821 	}
8822 }
8823 /**
8824  * Copies the specified text range to the clipboard.  The text will be placed
8825  * in the clipboard in plain text format and RTF format.
8826  *
8827  * @param start start index of the text
8828  * @param length length of text to place in clipboard
8829  *
8830  * @exception SWTError
8831  * @see org.eclipse.swt.dnd.Clipboard#setContents
8832  */
setClipboardContent(int start, int length, int clipboardType)8833 void setClipboardContent(int start, int length, int clipboardType) throws SWTError {
8834 	if (clipboardType == DND.SELECTION_CLIPBOARD && !IS_GTK) return;
8835 	TextTransfer plainTextTransfer = TextTransfer.getInstance();
8836 	TextWriter plainTextWriter = new TextWriter(start, length);
8837 	String plainText = getPlatformDelimitedText(plainTextWriter);
8838 	Object[] data;
8839 	Transfer[] types;
8840 	if (clipboardType == DND.SELECTION_CLIPBOARD) {
8841 		data = new Object[]{plainText};
8842 		types = new Transfer[]{plainTextTransfer};
8843 	} else {
8844 		RTFTransfer rtfTransfer = RTFTransfer.getInstance();
8845 		RTFWriter rtfWriter = new RTFWriter(start, length);
8846 		String rtfText = getPlatformDelimitedText(rtfWriter);
8847 		data = new Object[]{rtfText, plainText};
8848 		types = new Transfer[]{rtfTransfer, plainTextTransfer};
8849 	}
8850 	clipboard.setContents(data, types, clipboardType);
8851 }
8852 /**
8853  * Sets the content implementation to use for text storage.
8854  *
8855  * @param newContent StyledTextContent implementation to use for text storage.
8856  * @exception SWTException <ul>
8857  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8858  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8859  * </ul>
8860  * @exception IllegalArgumentException <ul>
8861  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
8862  * </ul>
8863  */
setContent(StyledTextContent newContent)8864 public void setContent(StyledTextContent newContent) {
8865 	checkWidget();
8866 	if (newContent == null) {
8867 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
8868 	}
8869 	if (content != null) {
8870 		content.removeTextChangeListener(textChangeListener);
8871 	}
8872 	content = newContent;
8873 	content.addTextChangeListener(textChangeListener);
8874 	reset();
8875 }
8876 /**
8877  * Sets the receiver's cursor to the cursor specified by the
8878  * argument.  Overridden to handle the null case since the
8879  * StyledText widget uses an ibeam as its default cursor.
8880  *
8881  * @see Control#setCursor(Cursor)
8882  */
8883 @Override
setCursor(Cursor cursor)8884 public void setCursor (Cursor cursor) {
8885 	checkWidget();
8886 	if (cursor != null && cursor.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
8887 	this.cursor = cursor;
8888 	if (cursor == null) {
8889 		Display display = getDisplay();
8890 		int type = blockSelection ? SWT.CURSOR_CROSS : SWT.CURSOR_IBEAM;
8891 		super.setCursor(display.getSystemCursor(type));
8892 	} else {
8893 		super.setCursor(cursor);
8894 	}
8895 }
8896 /**
8897  * Sets whether the widget implements double click mouse behavior.
8898  *
8899  * @param enable if true double clicking a word selects the word, if false
8900  * 	double clicks have the same effect as regular mouse clicks.
8901  * @exception SWTException <ul>
8902  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8903  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8904  * </ul>
8905  */
setDoubleClickEnabled(boolean enable)8906 public void setDoubleClickEnabled(boolean enable) {
8907 	checkWidget();
8908 	doubleClickEnabled = enable;
8909 }
8910 @Override
setDragDetect(boolean dragDetect)8911 public void setDragDetect (boolean dragDetect) {
8912 	checkWidget ();
8913 	this.dragDetect = dragDetect;
8914 }
8915 /**
8916  * Sets whether the widget content can be edited.
8917  *
8918  * @param editable if true content can be edited, if false content can not be
8919  * 	edited
8920  * @exception SWTException <ul>
8921  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8922  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8923  * </ul>
8924  */
setEditable(boolean editable)8925 public void setEditable(boolean editable) {
8926 	checkWidget();
8927 	this.editable = editable;
8928 }
8929 @Override
setEnabled(boolean enabled)8930 public void setEnabled(boolean enabled) {
8931 	super.setEnabled(enabled);
8932 	Display display = getDisplay();
8933 	this.enabled = enabled;
8934 	this.insideSetEnableCall = true;
8935 	try {
8936 		if (enabled) {
8937 			if (!customBackground) setBackground(display.getSystemColor(SWT.COLOR_LIST_BACKGROUND));
8938 			if (!customForeground) setForeground(display.getSystemColor(SWT.COLOR_LIST_FOREGROUND));
8939 		} else {
8940 			if (!customBackground) setBackground(display.getSystemColor(SWT.COLOR_TEXT_DISABLED_BACKGROUND));
8941 			if (!customForeground) setForeground(display.getSystemColor(SWT.COLOR_WIDGET_DISABLED_FOREGROUND));
8942 		}
8943 	}
8944 	finally {
8945 		this.insideSetEnableCall = false;
8946 	}
8947 }
8948 /**
8949  * Sets a new font to render text with.
8950  * <p>
8951  * <b>NOTE:</b> Italic fonts are not supported unless they have no overhang
8952  * and the same baseline as regular fonts.
8953  * </p>
8954  *
8955  * @param font new font
8956  * @exception SWTException <ul>
8957  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
8958  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
8959  * </ul>
8960  */
8961 @Override
setFont(Font font)8962 public void setFont(Font font) {
8963 	checkWidget();
8964 	int oldLineHeight = renderer.getLineHeight();
8965 	super.setFont(font);
8966 	renderer.setFont(getFont(), tabLength);
8967 	// keep the same top line visible. fixes 5815
8968 	if (isFixedLineHeight()) {
8969 		int lineHeight = renderer.getLineHeight();
8970 		if (lineHeight != oldLineHeight) {
8971 			int vscroll = (getVerticalScrollOffset() * lineHeight / oldLineHeight) - getVerticalScrollOffset();
8972 			scrollVertical(vscroll, true);
8973 		}
8974 	}
8975 	resetCache(0, content.getLineCount());
8976 	claimBottomFreeSpace();
8977 	calculateScrollBars();
8978 	if (isBidiCaret()) createCaretBitmaps();
8979 	caretDirection = SWT.NULL;
8980 	setCaretLocation();
8981 	super.redraw();
8982 }
8983 @Override
setForeground(Color color)8984 public void setForeground(Color color) {
8985 	checkWidget();
8986 	boolean foregroundDisabled = false;
8987 	if (!this.enabled && color == null) {
8988 		if (foreground != null) {
8989 			Color disabledFg = getDisplay().getSystemColor(SWT.COLOR_WIDGET_DISABLED_FOREGROUND);
8990 			if (foreground.equals(disabledFg)) {
8991 				return;
8992 			} else {
8993 				color = new Color (getDisplay(), disabledFg.getRGBA());
8994 				foregroundDisabled = true;
8995 			}
8996 		}
8997 	}
8998 	customForeground = color != null && !this.insideSetEnableCall && !foregroundDisabled;
8999 	foreground = color;
9000 	super.setForeground(color);
9001 	resetCache(0, content.getLineCount());
9002 	setCaretLocation();
9003 	super.redraw();
9004 }
9005 /**
9006  * Sets the horizontal scroll offset relative to the start of the line.
9007  * Do nothing if there is no text set.
9008  * <p>
9009  * <b>NOTE:</b> The horizontal index is reset to 0 when new text is set in the
9010  * widget.
9011  * </p>
9012  *
9013  * @param offset horizontal scroll offset relative to the start
9014  * 	of the line, measured in character increments starting at 0, if
9015  * 	equal to 0 the content is not scrolled, if &gt; 0 = the content is scrolled.
9016  * @exception SWTException <ul>
9017  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9018  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9019  * </ul>
9020  */
setHorizontalIndex(int offset)9021 public void setHorizontalIndex(int offset) {
9022 	checkWidget();
9023 	if (getCharCount() == 0) {
9024 		return;
9025 	}
9026 	if (offset < 0) {
9027 		offset = 0;
9028 	}
9029 	offset *= getHorizontalIncrement();
9030 	// allow any value if client area width is unknown or 0.
9031 	// offset will be checked in resize handler.
9032 	// don't use isVisible since width is known even if widget
9033 	// is temporarily invisible
9034 	if (clientAreaWidth > 0) {
9035 		int width = renderer.getWidth();
9036 		// prevent scrolling if the content fits in the client area.
9037 		// align end of longest line with right border of client area
9038 		// if offset is out of range.
9039 		if (offset > width - clientAreaWidth) {
9040 			offset = Math.max(0, width - clientAreaWidth);
9041 		}
9042 	}
9043 	scrollHorizontal(offset - horizontalScrollOffset, true);
9044 }
9045 /**
9046  * Sets the horizontal SWT logical point offset relative to the start of the line.
9047  * Do nothing if there is no text set.
9048  * <p>
9049  * <b>NOTE:</b> The horizontal SWT logical point offset is reset to 0 when new text
9050  * is set in the widget.
9051  * </p>
9052  *
9053  * @param pixel horizontal SWT logical point offset relative to the start
9054  * 	of the line.
9055  * @exception SWTException <ul>
9056  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9057  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9058  * </ul>
9059  * @since 2.0
9060  */
setHorizontalPixel(int pixel)9061 public void setHorizontalPixel(int pixel) {
9062 	checkWidget();
9063 	if (getCharCount() == 0) {
9064 		return;
9065 	}
9066 	if (pixel < 0) {
9067 		pixel = 0;
9068 	}
9069 	// allow any value if client area width is unknown or 0.
9070 	// offset will be checked in resize handler.
9071 	// don't use isVisible since width is known even if widget
9072 	// is temporarily invisible
9073 	if (clientAreaWidth > 0) {
9074 		int width = renderer.getWidth();
9075 		// prevent scrolling if the content fits in the client area.
9076 		// align end of longest line with right border of client area
9077 		// if offset is out of range.
9078 		if (pixel > width - clientAreaWidth) {
9079 			pixel = Math.max(0, width - clientAreaWidth);
9080 		}
9081 	}
9082 	scrollHorizontal(pixel - horizontalScrollOffset, true);
9083 }
9084 /**
9085  * Sets the line indentation of the widget.
9086  * <p>
9087  * It is the amount of blank space, in points, at the beginning of each line.
9088  * When a line wraps in several lines only the first one is indented.
9089  * </p>
9090  *
9091  * @param indent the new indent
9092  *
9093  * @exception SWTException <ul>
9094  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9095  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9096  * </ul>
9097  *
9098  * @see #setLineIndent(int, int, int)
9099  *
9100  * @since 3.2
9101  */
setIndent(int indent)9102 public void setIndent(int indent) {
9103 	checkWidget();
9104 	if (this.indent == indent || indent < 0) return;
9105 	this.indent = indent;
9106 	resetCache(0, content.getLineCount());
9107 	setCaretLocation();
9108 	super.redraw();
9109 }
9110 /**
9111  * Sets whether the widget should justify lines.
9112  *
9113  * @param justify whether lines should be justified
9114  *
9115  * @exception SWTException <ul>
9116  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9117  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9118  * </ul>
9119  *
9120  * @see #setLineJustify(int, int, boolean)
9121  *
9122  * @since 3.2
9123  */
setJustify(boolean justify)9124 public void setJustify(boolean justify) {
9125 	checkWidget();
9126 	if (this.justify == justify) return;
9127 	this.justify = justify;
9128 	resetCache(0, content.getLineCount());
9129 	setCaretLocation();
9130 	super.redraw();
9131 }
9132 /**
9133  * Maps a key to an action.
9134  * <p>
9135  * One action can be associated with N keys. However, each key can only
9136  * have one action (key:action is N:1 relation).
9137  * </p>
9138  *
9139  * @param key a key code defined in SWT.java or a character.
9140  * 	Optionally ORd with a state mask.  Preferred state masks are one or more of
9141  *  SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform
9142  *  differences.  However, there may be cases where using the specific state masks
9143  *  (i.e., SWT.CTRL, SWT.SHIFT, SWT.ALT, SWT.COMMAND) makes sense.
9144  * @param action one of the predefined actions defined in ST.java.
9145  * 	Use SWT.NULL to remove a key binding.
9146  * @exception SWTException <ul>
9147  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9148  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9149  * </ul>
9150  */
setKeyBinding(int key, int action)9151 public void setKeyBinding(int key, int action) {
9152 	checkWidget();
9153 	int modifierValue = key & SWT.MODIFIER_MASK;
9154 	int keyInt = key & SWT.KEY_MASK;
9155 	char keyChar = (char)keyInt;
9156 	/**
9157 	 * Bug 440535: Make sure the key getting mapped to letter is in defiened
9158 	 * character range and filter out incorrect int to char typecasting. For
9159 	 * Example: SWT.KEYPAD_CR int gets wrongly type-cast to char letter 'p'
9160 	 */
9161 	if (Character.isDefined(keyInt) && Character.isLetter(keyChar)) {
9162 		// make the keybinding case insensitive by adding it
9163 		// in its upper and lower case form
9164 		char ch = Character.toUpperCase(keyChar);
9165 		int newKey = ch | modifierValue;
9166 		if (action == SWT.NULL) {
9167 			keyActionMap.remove(newKey);
9168 		} else {
9169 			keyActionMap.put(newKey, action);
9170 		}
9171 		ch = Character.toLowerCase(keyChar);
9172 		newKey = ch | modifierValue;
9173 		if (action == SWT.NULL) {
9174 			keyActionMap.remove(newKey);
9175 		} else {
9176 			keyActionMap.put(newKey, action);
9177 		}
9178 	} else {
9179 		if (action == SWT.NULL) {
9180 			keyActionMap.remove(key);
9181 		} else {
9182 			keyActionMap.put(key, action);
9183 		}
9184 	}
9185 }
9186 /**
9187  * Sets the left margin.
9188  *
9189  * @param leftMargin the left margin.
9190  * @exception SWTException <ul>
9191  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9192  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9193  * </ul>
9194  *
9195  * @since 3.5
9196  */
setLeftMargin(int leftMargin)9197 public void setLeftMargin (int leftMargin) {
9198 	checkWidget();
9199 	setMargins(leftMargin, topMargin, rightMargin, bottomMargin);
9200 }
9201 /**
9202  * Sets the alignment of the specified lines. The argument should be one of <code>SWT.LEFT</code>,
9203  * <code>SWT.CENTER</code> or <code>SWT.RIGHT</code>.
9204  * <p>
9205  * Note that if <code>SWT.MULTI</code> is set, then <code>SWT.WRAP</code> must also be set
9206  * in order to stabilize the right edge before setting alignment.
9207  * </p><p>
9208  * Should not be called if a LineStyleListener has been set since the listener
9209  * maintains the line attributes.
9210  * </p><p>
9211  * All line attributes are maintained relative to the line text, not the
9212  * line index that is specified in this method call.
9213  * During text changes, when entire lines are inserted or removed, the line
9214  * attributes that are associated with the lines after the change
9215  * will "move" with their respective text. An entire line is defined as
9216  * extending from the first character on a line to the last and including the
9217  * line delimiter.
9218  * </p>
9219  * When two lines are joined by deleting a line delimiter, the top line
9220  * attributes take precedence and the attributes of the bottom line are deleted.
9221  * For all other text changes line attributes will remain unchanged.
9222  *
9223  * @param startLine first line the alignment is applied to, 0 based
9224  * @param lineCount number of lines the alignment applies to.
9225  * @param alignment line alignment
9226  *
9227  * @exception SWTException <ul>
9228  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9229  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9230  * </ul>
9231  * @exception IllegalArgumentException <ul>
9232  *   <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9233  * </ul>
9234  * @see #setAlignment(int)
9235  * @since 3.2
9236  */
setLineAlignment(int startLine, int lineCount, int alignment)9237 public void setLineAlignment(int startLine, int lineCount, int alignment) {
9238 	checkWidget();
9239 	if (isListening(ST.LineGetStyle)) return;
9240 	if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9241 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9242 	}
9243 
9244 	renderer.setLineAlignment(startLine, lineCount, alignment);
9245 	resetCache(startLine, lineCount);
9246 	redrawLines(startLine, lineCount, false);
9247 	int caretLine = getCaretLine();
9248 	if (startLine <= caretLine && caretLine < startLine + lineCount) {
9249 		setCaretLocation();
9250 	}
9251 	setAlignment();
9252 }
9253 /**
9254  * Sets the background color of the specified lines.
9255  * <p>
9256  * The background color is drawn for the width of the widget. All
9257  * line background colors are discarded when setText is called.
9258  * The text background color if defined in a StyleRange overlays the
9259  * line background color.
9260  * </p><p>
9261  * Should not be called if a LineBackgroundListener has been set since the
9262  * listener maintains the line backgrounds.
9263  * </p><p>
9264  * All line attributes are maintained relative to the line text, not the
9265  * line index that is specified in this method call.
9266  * During text changes, when entire lines are inserted or removed, the line
9267  * attributes that are associated with the lines after the change
9268  * will "move" with their respective text. An entire line is defined as
9269  * extending from the first character on a line to the last and including the
9270  * line delimiter.
9271  * </p><p>
9272  * When two lines are joined by deleting a line delimiter, the top line
9273  * attributes take precedence and the attributes of the bottom line are deleted.
9274  * For all other text changes line attributes will remain unchanged.
9275  * </p>
9276  *
9277  * @param startLine first line the color is applied to, 0 based
9278  * @param lineCount number of lines the color applies to.
9279  * @param background line background color
9280  * @exception SWTException <ul>
9281  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9282  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9283  * </ul>
9284  * @exception IllegalArgumentException <ul>
9285  *   <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9286  * </ul>
9287  */
setLineBackground(int startLine, int lineCount, Color background)9288 public void setLineBackground(int startLine, int lineCount, Color background) {
9289 	checkWidget();
9290 	if (isListening(ST.LineGetBackground)) return;
9291 	if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9292 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9293 	}
9294 	if (background != null) {
9295 		renderer.setLineBackground(startLine, lineCount, background);
9296 	} else {
9297 		renderer.clearLineBackground(startLine, lineCount);
9298 	}
9299 	redrawLines(startLine, lineCount, false);
9300 }
9301 /**
9302  * Sets the bullet of the specified lines.
9303  * <p>
9304  * Should not be called if a LineStyleListener has been set since the listener
9305  * maintains the line attributes.
9306  * </p><p>
9307  * All line attributes are maintained relative to the line text, not the
9308  * line index that is specified in this method call.
9309  * During text changes, when entire lines are inserted or removed, the line
9310  * attributes that are associated with the lines after the change
9311  * will "move" with their respective text. An entire line is defined as
9312  * extending from the first character on a line to the last and including the
9313  * line delimiter.
9314  * </p><p>
9315  * When two lines are joined by deleting a line delimiter, the top line
9316  * attributes take precedence and the attributes of the bottom line are deleted.
9317  * For all other text changes line attributes will remain unchanged.
9318  * </p>
9319  *
9320  * @param startLine first line the bullet is applied to, 0 based
9321  * @param lineCount number of lines the bullet applies to.
9322  * @param bullet line bullet
9323  *
9324  * @exception SWTException <ul>
9325  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9326  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9327  * </ul>
9328  * @exception IllegalArgumentException <ul>
9329  *   <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9330  * </ul>
9331  * @since 3.2
9332  */
setLineBullet(int startLine, int lineCount, Bullet bullet)9333 public void setLineBullet(int startLine, int lineCount, Bullet bullet) {
9334 	checkWidget();
9335 	if (isListening(ST.LineGetStyle)) return;
9336 	if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9337 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9338 	}
9339 	int oldBottom = getLinePixel(startLine + lineCount);
9340 	renderer.setLineBullet(startLine, lineCount, bullet);
9341 	resetCache(startLine, lineCount);
9342 	int newBottom = getLinePixel(startLine + lineCount);
9343 	redrawLines(startLine, lineCount, oldBottom != newBottom);
9344 	int caretLine = getCaretLine();
9345 	if (startLine <= caretLine && caretLine < startLine + lineCount) {
9346 		setCaretLocation();
9347 	}
9348 }
9349 /**
9350  * Returns true if StyledText is in word wrap mode and false otherwise.
9351  *
9352  * @return true if StyledText is in word wrap mode and false otherwise.
9353  */
isWordWrap()9354 boolean isWordWrap() {
9355 	return wordWrap || visualWrap;
9356 }
9357 /**
9358  * Sets the indent of the specified lines.
9359  * <p>
9360  * Should not be called if a LineStyleListener has been set since the listener
9361  * maintains the line attributes.
9362  * </p><p>
9363  * All line attributes are maintained relative to the line text, not the
9364  * line index that is specified in this method call.
9365  * During text changes, when entire lines are inserted or removed, the line
9366  * attributes that are associated with the lines after the change
9367  * will "move" with their respective text. An entire line is defined as
9368  * extending from the first character on a line to the last and including the
9369  * line delimiter.
9370  * </p><p>
9371  * When two lines are joined by deleting a line delimiter, the top line
9372  * attributes take precedence and the attributes of the bottom line are deleted.
9373  * For all other text changes line attributes will remain unchanged.
9374  * </p>
9375  *
9376  * @param startLine first line the indent is applied to, 0 based
9377  * @param lineCount number of lines the indent applies to.
9378  * @param indent line indent
9379  *
9380  * @exception SWTException <ul>
9381  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9382  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9383  * </ul>
9384  * @exception IllegalArgumentException <ul>
9385  *   <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9386  * </ul>
9387  * @see #setIndent(int)
9388  * @since 3.2
9389  */
setLineIndent(int startLine, int lineCount, int indent)9390 public void setLineIndent(int startLine, int lineCount, int indent) {
9391 	checkWidget();
9392 	if (isListening(ST.LineGetStyle)) return;
9393 	if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9394 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9395 	}
9396 	int oldBottom = getLinePixel(startLine + lineCount);
9397 	renderer.setLineIndent(startLine, lineCount, indent);
9398 	resetCache(startLine, lineCount);
9399 	int newBottom = getLinePixel(startLine + lineCount);
9400 	redrawLines(startLine, lineCount, oldBottom != newBottom);
9401 	int caretLine = getCaretLine();
9402 	if (startLine <= caretLine && caretLine < startLine + lineCount) {
9403 		setCaretLocation();
9404 	}
9405 }
9406 
9407 /**
9408  * Sets the vertical indent of the specified lines.
9409  * <p>
9410  * Should not be called if a LineStyleListener has been set since the listener
9411  * maintains the line attributes.
9412  * </p><p>
9413  * All line attributes are maintained relative to the line text, not the
9414  * line index that is specified in this method call.
9415  * During text changes, when entire lines are inserted or removed, the line
9416  * attributes that are associated with the lines after the change
9417  * will "move" with their respective text. An entire line is defined as
9418  * extending from the first character on a line to the last and including the
9419  * line delimiter.
9420  * </p><p>
9421  * When two lines are joined by deleting a line delimiter, the top line
9422  * attributes take precedence and the attributes of the bottom line are deleted.
9423  * For all other text changes line attributes will remain unchanged.
9424  * </p><p>
9425  * Setting both line spacing and vertical indent on a line would result in the
9426  * spacing and indent add up for the line.
9427  * </p>
9428  *
9429  * @param lineIndex line index the vertical indent is applied to, 0 based
9430  * @param verticalLineIndent vertical line indent
9431  *
9432  * @exception SWTException <ul>
9433  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9434  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9435  * </ul>
9436  * @exception IllegalArgumentException <ul>
9437  *   <li>ERROR_INVALID_ARGUMENT when the specified line index is invalid</li>
9438  * </ul>
9439  * @since 3.109
9440  */
setLineVerticalIndent(int lineIndex, int verticalLineIndent)9441 public void setLineVerticalIndent(int lineIndex, int verticalLineIndent) {
9442 	checkWidget();
9443 	if (isListening(ST.LineGetStyle)) return;
9444 	if (lineIndex < 0 || lineIndex >= content.getLineCount()) {
9445 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9446 	}
9447 	if (verticalLineIndent == renderer.getLineVerticalIndent(lineIndex)) {
9448 			return;
9449 	}
9450 	int oldBottom = getLinePixel(lineIndex + 1);
9451 	if (oldBottom <= getClientArea().height) {
9452 		verticalScrollOffset = -1;
9453 	}
9454 	renderer.setLineVerticalIndent(lineIndex, verticalLineIndent);
9455 	hasVerticalIndent = verticalLineIndent != 0 || renderer.hasVerticalIndent();
9456 	resetCache(lineIndex, 1);
9457 	int newBottom = getLinePixel(lineIndex + 1);
9458 	redrawLines(lineIndex, 1, oldBottom != newBottom);
9459 	int caretLine = getCaretLine();
9460 	if (lineIndex <= caretLine && caretLine < lineIndex + 1) {
9461 		setCaretLocation();
9462 	}
9463 }
9464 
9465 /**
9466  * Sets the justify of the specified lines.
9467  * <p>
9468  * Should not be called if a LineStyleListener has been set since the listener
9469  * maintains the line attributes.
9470  * </p><p>
9471  * All line attributes are maintained relative to the line text, not the
9472  * line index that is specified in this method call.
9473  * During text changes, when entire lines are inserted or removed, the line
9474  * attributes that are associated with the lines after the change
9475  * will "move" with their respective text. An entire line is defined as
9476  * extending from the first character on a line to the last and including the
9477  * line delimiter.
9478  * </p><p>
9479  * When two lines are joined by deleting a line delimiter, the top line
9480  * attributes take precedence and the attributes of the bottom line are deleted.
9481  * For all other text changes line attributes will remain unchanged.
9482  * </p>
9483  *
9484  * @param startLine first line the justify is applied to, 0 based
9485  * @param lineCount number of lines the justify applies to.
9486  * @param justify true if lines should be justified
9487  *
9488  * @exception SWTException <ul>
9489  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9490  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9491  * </ul>
9492  * @exception IllegalArgumentException <ul>
9493  *   <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9494  * </ul>
9495  * @see #setJustify(boolean)
9496  * @since 3.2
9497  */
setLineJustify(int startLine, int lineCount, boolean justify)9498 public void setLineJustify(int startLine, int lineCount, boolean justify) {
9499 	checkWidget();
9500 	if (isListening(ST.LineGetStyle)) return;
9501 	if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9502 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9503 	}
9504 
9505 	renderer.setLineJustify(startLine, lineCount, justify);
9506 	resetCache(startLine, lineCount);
9507 	redrawLines(startLine, lineCount, false);
9508 	int caretLine = getCaretLine();
9509 	if (startLine <= caretLine && caretLine < startLine + lineCount) {
9510 		setCaretLocation();
9511 	}
9512 }
9513 /**
9514  * Sets the line spacing of the widget. The line spacing applies for all lines.
9515  * In the case of #setLineSpacingProvider(StyledTextLineSpacingProvider) is customized,
9516  * the line spacing are applied only for the lines which are not managed by {@link StyledTextLineSpacingProvider}.
9517  *
9518  * @param lineSpacing the line spacing
9519  * @exception SWTException <ul>
9520  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9521  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9522  * </ul>
9523  * @see #setLineSpacingProvider(StyledTextLineSpacingProvider)
9524  * @since 3.2
9525  */
setLineSpacing(int lineSpacing)9526 public void setLineSpacing(int lineSpacing) {
9527 	checkWidget();
9528 	if (this.lineSpacing == lineSpacing || lineSpacing < 0) return;
9529 	this.lineSpacing = lineSpacing;
9530 	resetCache(0, content.getLineCount());
9531 	setCaretLocation();
9532 	super.redraw();
9533 }
9534 /**
9535  * Sets the line spacing provider of the widget. The line spacing applies for some lines with customized spacing
9536  * or reset the customized spacing if the argument is null.
9537  *
9538  * @param lineSpacingProvider the line spacing provider (or null)
9539  * @exception SWTException <ul>
9540  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9541  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9542  * </ul>
9543  * @see #setLineSpacingProvider(StyledTextLineSpacingProvider)
9544  * @since 3.107
9545  */
setLineSpacingProvider(StyledTextLineSpacingProvider lineSpacingProvider)9546 public void setLineSpacingProvider(StyledTextLineSpacingProvider lineSpacingProvider) {
9547 	checkWidget();
9548 	boolean wasFixedLineHeight = isFixedLineHeight();
9549 	if (renderer.getLineSpacingProvider() == null && lineSpacingProvider == null
9550 			|| (renderer.getLineSpacingProvider() != null
9551 					&& renderer.getLineSpacingProvider().equals(lineSpacingProvider)))
9552 		return;
9553 	renderer.setLineSpacingProvider(lineSpacingProvider);
9554 	// reset lines cache if needed
9555 	if (lineSpacingProvider == null) {
9556 		if (!wasFixedLineHeight) {
9557 			resetCache(0, content.getLineCount());
9558 		}
9559 	} else {
9560 		if (wasFixedLineHeight) {
9561 			int firstLine = -1;
9562 			for (int i = 0; i < content.getLineCount(); i++) {
9563 				Integer lineSpacing = lineSpacingProvider.getLineSpacing(i);
9564 				if (lineSpacing != null && lineSpacing > 0) {
9565 					// there is a custom line spacing, set StyledText as variable line height mode
9566 					// reset only the line size
9567 					renderer.reset(i, 1);
9568 					if (firstLine == -1) {
9569 						firstLine = i;
9570 					}
9571 				}
9572 			}
9573 			if (firstLine != -1) {
9574 				// call reset cache for the first line which have changed to recompute scrollbars
9575 				resetCache(firstLine, 0);
9576 			}
9577 		}
9578 	}
9579 	setCaretLocation();
9580 	super.redraw();
9581 }
9582 /**
9583  * Sets the tab stops of the specified lines.
9584  * <p>
9585  * Should not be called if a <code>LineStyleListener</code> has been set since the listener
9586  * maintains the line attributes.
9587  * </p><p>
9588  * All line attributes are maintained relative to the line text, not the
9589  * line index that is specified in this method call.
9590  * During text changes, when entire lines are inserted or removed, the line
9591  * attributes that are associated with the lines after the change
9592  * will "move" with their respective text. An entire line is defined as
9593  * extending from the first character on a line to the last and including the
9594  * line delimiter.
9595  * </p><p>
9596  * When two lines are joined by deleting a line delimiter, the top line
9597  * attributes take precedence and the attributes of the bottom line are deleted.
9598  * For all other text changes line attributes will remain unchanged.
9599  * </p>
9600  *
9601  * @param startLine first line the justify is applied to, 0 based
9602  * @param lineCount number of lines the justify applies to.
9603  * @param tabStops tab stops
9604  *
9605  * @exception SWTException <ul>
9606  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9607  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9608  * </ul>
9609  * @exception IllegalArgumentException <ul>
9610  *   <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9611  * </ul>
9612  * @see #setTabStops(int[])
9613  * @since 3.6
9614  */
setLineTabStops(int startLine, int lineCount, int[] tabStops)9615 public void setLineTabStops(int startLine, int lineCount, int[] tabStops) {
9616 	checkWidget();
9617 	if (isListening(ST.LineGetStyle)) return;
9618 	if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9619 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9620 	}
9621 	if (tabStops != null) {
9622 		int pos = 0;
9623 		int[] newTabs = new int[tabStops.length];
9624 		for (int i = 0; i < tabStops.length; i++) {
9625 			if (tabStops[i] < pos) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9626 			newTabs[i] = pos = tabStops[i];
9627 		}
9628 		renderer.setLineTabStops(startLine, lineCount, newTabs);
9629 	} else {
9630 		renderer.setLineTabStops(startLine, lineCount, null);
9631 	}
9632 	resetCache(startLine, lineCount);
9633 	redrawLines(startLine, lineCount, false);
9634 	int caretLine = getCaretLine();
9635 	if (startLine <= caretLine && caretLine < startLine + lineCount) {
9636 		setCaretLocation();
9637 	}
9638 }
9639 /**
9640  * Sets the wrap indent of the specified lines.
9641  * <p>
9642  * Should not be called if a <code>LineStyleListener</code> has been set since the listener
9643  * maintains the line attributes.
9644  * </p><p>
9645  * All line attributes are maintained relative to the line text, not the
9646  * line index that is specified in this method call.
9647  * During text changes, when entire lines are inserted or removed, the line
9648  * attributes that are associated with the lines after the change
9649  * will "move" with their respective text. An entire line is defined as
9650  * extending from the first character on a line to the last and including the
9651  * line delimiter.
9652  * </p><p>
9653  * When two lines are joined by deleting a line delimiter, the top line
9654  * attributes take precedence and the attributes of the bottom line are deleted.
9655  * For all other text changes line attributes will remain unchanged.
9656  * </p>
9657  *
9658  * @param startLine first line the wrap indent is applied to, 0 based
9659  * @param lineCount number of lines the wrap indent applies to.
9660  * @param wrapIndent line wrap indent
9661  *
9662  * @exception SWTException <ul>
9663  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9664  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9665  * </ul>
9666  * @exception IllegalArgumentException <ul>
9667  *   <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
9668  * </ul>
9669  * @see #setWrapIndent(int)
9670  * @since 3.6
9671  */
setLineWrapIndent(int startLine, int lineCount, int wrapIndent)9672 public void setLineWrapIndent(int startLine, int lineCount, int wrapIndent) {
9673 	checkWidget();
9674 	if (isListening(ST.LineGetStyle)) return;
9675 	if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
9676 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9677 	}
9678 	int oldBottom = getLinePixel(startLine + lineCount);
9679 	renderer.setLineWrapIndent(startLine, lineCount, wrapIndent);
9680 	resetCache(startLine, lineCount);
9681 	int newBottom = getLinePixel(startLine + lineCount);
9682 	redrawLines(startLine, lineCount, oldBottom != newBottom);
9683 	int caretLine = getCaretLine();
9684 	if (startLine <= caretLine && caretLine < startLine + lineCount) {
9685 		setCaretLocation();
9686 	}
9687 }
9688 
9689 /**
9690  * Sets the color of the margins.
9691  *
9692  * @param color the new color (or null)
9693  * @exception IllegalArgumentException <ul>
9694  *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
9695  * </ul>
9696  * @exception SWTException <ul>
9697  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9698  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9699  * </ul>
9700  *
9701  * @since 3.5
9702  */
setMarginColor(Color color)9703 public void setMarginColor(Color color) {
9704 	checkWidget();
9705 	if (color != null && color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9706 	marginColor = color;
9707 	super.redraw();
9708 }
9709 /**
9710  * Sets the margins.
9711  *
9712  * @param leftMargin the left margin.
9713  * @param topMargin the top margin.
9714  * @param rightMargin the right margin.
9715  * @param bottomMargin the bottom margin.
9716  * @exception SWTException <ul>
9717  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9718  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9719  * </ul>
9720  *
9721  * @since 3.5
9722  */
setMargins(int leftMargin, int topMargin, int rightMargin, int bottomMargin)9723 public void setMargins (int leftMargin, int topMargin, int rightMargin, int bottomMargin) {
9724 	checkWidget();
9725 	this.leftMargin = Math.max(0, leftMargin) + alignmentMargin;
9726 	this.topMargin = Math.max(0, topMargin);
9727 	this.rightMargin = Math.max(0, rightMargin);
9728 	this.bottomMargin = Math.max(0, bottomMargin);
9729 	resetCache(0, content.getLineCount());
9730 	setScrollBars(true);
9731 	setCaretLocation();
9732 	setAlignment();
9733 	super.redraw();
9734 }
9735 /**
9736  * Sets the enabled state of the mouse navigator. When the mouse navigator is enabled, the user can navigate through the widget
9737  * by pressing the middle button and moving the cursor.
9738  *
9739  * @param enabled if true, the mouse navigator is enabled, if false the mouse navigator is deactivated
9740  * @exception SWTException <ul>
9741  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9742  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9743  * </ul>
9744  * @since 3.110
9745  */
setMouseNavigatorEnabled(boolean enabled)9746 public void setMouseNavigatorEnabled(boolean enabled) {
9747 	checkWidget();
9748 	if ((enabled && mouseNavigator != null) || (!enabled && mouseNavigator == null)) {
9749 		return;
9750 	}
9751 	if (enabled) {
9752 		mouseNavigator = new MouseNavigator(this);
9753 	} else {
9754 		mouseNavigator.dispose();
9755 		mouseNavigator = null;
9756 	}
9757 }
9758 /**
9759  * Flips selection anchor based on word selection direction.
9760  */
setMouseWordSelectionAnchor()9761 void setMouseWordSelectionAnchor() {
9762 	if (doubleClickEnabled && clickCount > 1) {
9763 		if (caretOffset < doubleClickSelection.x) {
9764 			selectionAnchor = doubleClickSelection.y;
9765 		} else if (caretOffset > doubleClickSelection.y) {
9766 			selectionAnchor = doubleClickSelection.x;
9767 		}
9768 	}
9769 }
9770 /**
9771  * Sets the orientation of the receiver, which must be one
9772  * of the constants <code>SWT.LEFT_TO_RIGHT</code> or <code>SWT.RIGHT_TO_LEFT</code>.
9773  *
9774  * @param orientation new orientation style
9775  *
9776  * @exception SWTException <ul>
9777  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9778  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9779  * </ul>
9780  *
9781  * @since 2.1.2
9782  */
9783 @Override
setOrientation(int orientation)9784 public void setOrientation(int orientation) {
9785 	int oldOrientation = getOrientation();
9786 	super.setOrientation(orientation);
9787 	int newOrientation = getOrientation();
9788 	if (oldOrientation != newOrientation) {
9789 		resetBidiData();
9790 	}
9791 }
9792 /**
9793  * Sets the right margin.
9794  *
9795  * @param rightMargin the right margin.
9796  * @exception SWTException <ul>
9797  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9798  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9799  * </ul>
9800  *
9801  * @since 3.5
9802  */
setRightMargin(int rightMargin)9803 public void setRightMargin (int rightMargin) {
9804 	checkWidget();
9805 	setMargins(getLeftMargin(), topMargin, rightMargin, bottomMargin);
9806 }
setScrollBar(ScrollBar bar, int clientArea, int maximum, int margin)9807 void setScrollBar(ScrollBar bar, int clientArea, int maximum, int margin) {
9808 	int inactive = 1;
9809 	if (clientArea < maximum) {
9810 		bar.setMaximum(maximum - margin);
9811 		bar.setThumb(clientArea - margin);
9812 		bar.setPageIncrement(clientArea - margin);
9813 		if (!alwaysShowScroll) bar.setVisible(true);
9814 	} else if (bar.getThumb() != inactive || bar.getMaximum() != inactive) {
9815 		bar.setValues(bar.getSelection(), bar.getMinimum(), inactive, inactive, bar.getIncrement(), inactive);
9816 	}
9817 }
9818 /**
9819  * Adjusts the maximum and the page size of the scroll bars to
9820  * reflect content width/length changes.
9821  *
9822  * @param vertical indicates if the vertical scrollbar also needs to be set
9823  */
setScrollBars(boolean vertical)9824 void setScrollBars(boolean vertical) {
9825 	ignoreResize++;
9826 	if (!isFixedLineHeight() || !alwaysShowScroll) vertical = true;
9827 	ScrollBar verticalBar = vertical ? getVerticalBar() : null;
9828 	ScrollBar horizontalBar = getHorizontalBar();
9829 	int oldHeight = clientAreaHeight;
9830 	int oldWidth = clientAreaWidth;
9831 	if (!alwaysShowScroll) {
9832 		if (verticalBar != null) verticalBar.setVisible(false);
9833 		if (horizontalBar != null) horizontalBar.setVisible(false);
9834 	}
9835 	if (verticalBar != null) {
9836 		setScrollBar(verticalBar, clientAreaHeight, renderer.getHeight(), topMargin + bottomMargin);
9837 	}
9838 	if (horizontalBar != null && !wordWrap) {
9839 		setScrollBar(horizontalBar, clientAreaWidth, renderer.getWidth(), leftMargin + rightMargin);
9840 		if (!alwaysShowScroll && horizontalBar.getVisible() && verticalBar != null) {
9841 			setScrollBar(verticalBar, clientAreaHeight, renderer.getHeight(), topMargin + bottomMargin);
9842 			if (verticalBar.getVisible()) {
9843 				setScrollBar(horizontalBar, clientAreaWidth, renderer.getWidth(), leftMargin + rightMargin);
9844 			}
9845 		}
9846 	}
9847 	if (!alwaysShowScroll) {
9848 		redrawMargins(oldHeight, oldWidth);
9849 	}
9850 	ignoreResize--;
9851 }
9852 /**
9853  * Sets the selection to the given position and scrolls it into view.  Equivalent to setSelection(start,start).
9854  *
9855  * @param start new caret position
9856  * @see #setSelection(int,int)
9857  * @exception SWTException <ul>
9858  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9859  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9860  * </ul>
9861  * @exception IllegalArgumentException <ul>
9862  *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
9863  * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
9864  * </ul>
9865  */
setSelection(int start)9866 public void setSelection(int start) {
9867 	// checkWidget test done in setSelectionRange
9868 	setSelection(start, start);
9869 }
9870 /**
9871  * Sets the selection and scrolls it into view.
9872  * <p>
9873  * Indexing is zero based.  Text selections are specified in terms of
9874  * caret positions.  In a text widget that contains N characters, there are
9875  * N+1 caret positions, ranging from 0..N
9876  * </p>
9877  *
9878  * @param point x=selection start offset, y=selection end offset
9879  * 	The caret will be placed at the selection start when x &gt; y.
9880  * @see #setSelection(int,int)
9881  * @exception SWTException <ul>
9882  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9883  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9884  * </ul>
9885  * @exception IllegalArgumentException <ul>
9886  *   <li>ERROR_NULL_ARGUMENT when point is null</li>
9887  *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
9888  * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
9889  * </ul>
9890  */
setSelection(Point point)9891 public void setSelection(Point point) {
9892 	checkWidget();
9893 	if (point == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
9894 	setSelection(point.x, point.y);
9895 }
9896 /**
9897  * Sets the receiver's selection background color to the color specified
9898  * by the argument, or to the default system color for the control
9899  * if the argument is null.
9900  *
9901  * @param color the new color (or null)
9902  *
9903  * @exception IllegalArgumentException <ul>
9904  *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
9905  * </ul>
9906  * @exception SWTException <ul>
9907  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9908  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9909  * </ul>
9910  * @since 2.1
9911  */
setSelectionBackground(Color color)9912 public void setSelectionBackground (Color color) {
9913 	checkWidget ();
9914 	if (color != null) {
9915 		if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9916 	}
9917 	selectionBackground = color;
9918 	resetCache(0, content.getLineCount());
9919 	setCaretLocation();
9920 	super.redraw();
9921 }
9922 /**
9923  * Sets the receiver's selection foreground color to the color specified
9924  * by the argument, or to the default system color for the control
9925  * if the argument is null.
9926  * <p>
9927  * Note that this is a <em>HINT</em>. Some platforms do not allow the application
9928  * to change the selection foreground color.
9929  * </p>
9930  * @param color the new color (or null)
9931  *
9932  * @exception IllegalArgumentException <ul>
9933  *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
9934  * </ul>
9935  * @exception SWTException <ul>
9936  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9937  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9938  * </ul>
9939  * @since 2.1
9940  */
setSelectionForeground(Color color)9941 public void setSelectionForeground (Color color) {
9942 	checkWidget ();
9943 	if (color != null) {
9944 		if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
9945 	}
9946 	selectionForeground = color;
9947 	resetCache(0, content.getLineCount());
9948 	setCaretLocation();
9949 	super.redraw();
9950 }
9951 /**
9952  * Sets the selection and scrolls it into view.
9953  * <p>
9954  * Indexing is zero based.  Text selections are specified in terms of
9955  * caret positions.  In a text widget that contains N characters, there are
9956  * N+1 caret positions, ranging from 0..N
9957  * </p>
9958  *
9959  * @param start selection start offset. The caret will be placed at the
9960  * 	selection start when start &gt; end.
9961  * @param end selection end offset
9962  * @see #setSelectionRange(int,int)
9963  * @exception SWTException <ul>
9964  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
9965  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
9966  * </ul>
9967  * @exception IllegalArgumentException <ul>
9968  *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
9969  * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
9970  * </ul>
9971  */
setSelection(int start, int end)9972 public void setSelection(int start, int end) {
9973 	setSelectionRange(start, end - start);
9974 	showSelection();
9975 }
9976 /**
9977  * Sets the selection.
9978  * <p>
9979  * The new selection may not be visible. Call showSelection to scroll
9980  * the selection into view.
9981  * </p>
9982  *
9983  * @param start offset of the first selected character, start &gt;= 0 must be true.
9984  * @param length number of characters to select, 0 &lt;= start + length
9985  * 	&lt;= getCharCount() must be true.
9986  * 	A negative length places the caret at the selection start.
9987  * @param sendEvent a Selection event is sent when set to true and when
9988  * 	the selection is reset.
9989  */
setSelection(int start, int length, boolean sendEvent, boolean doBlock)9990 void setSelection(int start, int length, boolean sendEvent, boolean doBlock) {
9991 	int end = start + length;
9992 	if (start > end) {
9993 		int temp = end;
9994 		end = start;
9995 		start = temp;
9996 	}
9997 	// is the selection range different or is the selection direction
9998 	// different?
9999 	if (selection.x != start || selection.y != end ||
10000 		(length > 0 && selectionAnchor != selection.x) ||
10001 		(length < 0 && selectionAnchor != selection.y)) {
10002 		if (blockSelection && doBlock) {
10003 			if (length < 0) {
10004 				setBlockSelectionOffset(end, start, sendEvent);
10005 			} else {
10006 				setBlockSelectionOffset(start, end, sendEvent);
10007 			}
10008 		} else {
10009 			int oldStart = selection.x;
10010 			int oldLength = selection.y - selection.x;
10011 			int charCount = content.getCharCount();
10012 			// called internally to remove selection after text is removed
10013 			// therefore make sure redraw range is valid.
10014 			int redrawX = Math.min(selection.x, charCount);
10015 			int redrawY = Math.min(selection.y, charCount);
10016 			if (length < 0) {
10017 				selectionAnchor = selection.y = end;
10018 				selection.x = start;
10019 				setCaretOffset(start, PREVIOUS_OFFSET_TRAILING);
10020 			} else {
10021 				selectionAnchor = selection.x = start;
10022 				selection.y = end;
10023 				setCaretOffset(end, PREVIOUS_OFFSET_TRAILING);
10024 			}
10025 			redrawX = Math.min(redrawX, selection.x);
10026 			redrawY = Math.max(redrawY, selection.y);
10027 			if (redrawY - redrawX > 0) {
10028 				internalRedrawRange(redrawX, redrawY - redrawX);
10029 			}
10030 			if (sendEvent && (oldLength != end - start || (oldLength != 0 && oldStart != start))) {
10031 				sendSelectionEvent();
10032 			}
10033 			sendAccessibleTextCaretMoved();
10034 		}
10035 	}
10036 }
10037 /**
10038  * Sets the selection.
10039  * <p>
10040  * The new selection may not be visible. Call showSelection to scroll the selection
10041  * into view. A negative length places the caret at the visual start of the selection.
10042  * </p>
10043  *
10044  * @param start offset of the first selected character
10045  * @param length number of characters to select
10046  *
10047  * @exception SWTException <ul>
10048  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10049  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10050  * </ul>
10051  * @exception IllegalArgumentException <ul>
10052  *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
10053  * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
10054  * </ul>
10055  */
setSelectionRange(int start, int length)10056 public void setSelectionRange(int start, int length) {
10057 	checkWidget();
10058 	int contentLength = getCharCount();
10059 	start = Math.max(0, Math.min (start, contentLength));
10060 	int end = start + length;
10061 	if (end < 0) {
10062 		length = -start;
10063 	} else {
10064 		if (end > contentLength) length = contentLength - start;
10065 	}
10066 	if (isLineDelimiter(start) || isLineDelimiter(start + length)) {
10067 		// the start offset or end offset of the selection range is inside a
10068 		// multi byte line delimiter. This is an illegal operation and an exception
10069 		// is thrown. Fixes 1GDKK3R
10070 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
10071 	}
10072 	setSelection(start, length, false, true);
10073 	setCaretLocation();
10074 }
10075 /**
10076  * Adds the specified style.
10077  * <p>
10078  * The new style overwrites existing styles for the specified range.
10079  * Existing style ranges are adjusted if they partially overlap with
10080  * the new style. To clear an individual style, call setStyleRange
10081  * with a StyleRange that has null attributes.
10082  * </p><p>
10083  * Should not be called if a LineStyleListener has been set since the
10084  * listener maintains the styles.
10085  * </p>
10086  *
10087  * @param range StyleRange object containing the style information.
10088  * Overwrites the old style in the given range. May be null to delete
10089  * all styles.
10090  * @exception SWTException <ul>
10091  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10092  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10093  * </ul>
10094  * @exception IllegalArgumentException <ul>
10095  *   <li>ERROR_INVALID_RANGE when the style range is outside the valid range (&gt; getCharCount())</li>
10096  * </ul>
10097  */
setStyleRange(StyleRange range)10098 public void setStyleRange(StyleRange range) {
10099 	checkWidget();
10100 	if (isListening(ST.LineGetStyle)) return;
10101 	if (range != null) {
10102 		if (range.isUnstyled()) {
10103 			setStyleRanges(range.start, range.length, null, null, false);
10104 		} else {
10105 			setStyleRanges(range.start, 0, null, new StyleRange[]{range}, false);
10106 		}
10107 	} else {
10108 		setStyleRanges(0, 0, null, null, true);
10109 	}
10110 }
10111 /**
10112  * Clears the styles in the range specified by <code>start</code> and
10113  * <code>length</code> and adds the new styles.
10114  * <p>
10115  * The ranges array contains start and length pairs.  Each pair refers to
10116  * the corresponding style in the styles array.  For example, the pair
10117  * that starts at ranges[n] with length ranges[n+1] uses the style
10118  * at styles[n/2].  The range fields within each StyleRange are ignored.
10119  * If ranges or styles is null, the specified range is cleared.
10120  * </p><p>
10121  * Note: It is expected that the same instance of a StyleRange will occur
10122  * multiple times within the styles array, reducing memory usage.
10123  * </p><p>
10124  * Should not be called if a LineStyleListener has been set since the
10125  * listener maintains the styles.
10126  * </p>
10127  *
10128  * @param start offset of first character where styles will be deleted
10129  * @param length length of the range to delete styles in
10130  * @param ranges the array of ranges.  The ranges must not overlap and must be in order.
10131  * @param styles the array of StyleRanges.  The range fields within the StyleRange are unused.
10132  *
10133  * @exception SWTException <ul>
10134  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10135  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10136  * </ul>
10137  * @exception IllegalArgumentException <ul>
10138  *    <li>ERROR_NULL_ARGUMENT when an element in the styles array is null</li>
10139  *    <li>ERROR_INVALID_RANGE when the number of ranges and style do not match (ranges.length * 2 == styles.length)</li>
10140  *    <li>ERROR_INVALID_RANGE when a range is outside the valid range (&gt; getCharCount() or less than zero)</li>
10141  *    <li>ERROR_INVALID_RANGE when a range overlaps</li>
10142  * </ul>
10143  *
10144  * @since 3.2
10145  */
setStyleRanges(int start, int length, int[] ranges, StyleRange[] styles)10146 public void setStyleRanges(int start, int length, int[] ranges, StyleRange[] styles) {
10147 	checkWidget();
10148 	if (isListening(ST.LineGetStyle)) return;
10149 	if (ranges == null || styles == null) {
10150 		setStyleRanges(start, length, null, null, false);
10151 	} else {
10152 		setStyleRanges(start, length, ranges, styles, false);
10153 	}
10154 }
10155 /**
10156  * Sets styles to be used for rendering the widget content.
10157  * <p>
10158  * All styles in the widget will be replaced with the given set of ranges and styles.
10159  * The ranges array contains start and length pairs.  Each pair refers to
10160  * the corresponding style in the styles array.  For example, the pair
10161  * that starts at ranges[n] with length ranges[n+1] uses the style
10162  * at styles[n/2].  The range fields within each StyleRange are ignored.
10163  * If either argument is null, the styles are cleared.
10164  * </p><p>
10165  * Note: It is expected that the same instance of a StyleRange will occur
10166  * multiple times within the styles array, reducing memory usage.
10167  * </p><p>
10168  * Should not be called if a LineStyleListener has been set since the
10169  * listener maintains the styles.
10170  * </p>
10171  *
10172  * @param ranges the array of ranges.  The ranges must not overlap and must be in order.
10173  * @param styles the array of StyleRanges.  The range fields within the StyleRange are unused.
10174  *
10175  * @exception SWTException <ul>
10176  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10177  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10178  * </ul>
10179  * @exception IllegalArgumentException <ul>
10180  *    <li>ERROR_NULL_ARGUMENT when an element in the styles array is null</li>
10181  *    <li>ERROR_INVALID_RANGE when the number of ranges and style do not match (ranges.length * 2 == styles.length)</li>
10182  *    <li>ERROR_INVALID_RANGE when a range is outside the valid range (&gt; getCharCount() or less than zero)</li>
10183  *    <li>ERROR_INVALID_RANGE when a range overlaps</li>
10184  * </ul>
10185  *
10186  * @since 3.2
10187  */
setStyleRanges(int[] ranges, StyleRange[] styles)10188 public void setStyleRanges(int[] ranges, StyleRange[] styles) {
10189 	checkWidget();
10190 	if (isListening(ST.LineGetStyle)) return;
10191 	if (ranges == null || styles == null) {
10192 		setStyleRanges(0, 0, null, null, true);
10193 	} else {
10194 		setStyleRanges(0, 0, ranges, styles, true);
10195 	}
10196 }
setStyleRanges(int start, int length, int[] ranges, StyleRange[] styles, boolean reset)10197 void setStyleRanges(int start, int length, int[] ranges, StyleRange[] styles, boolean reset) {
10198 	int charCount = content.getCharCount();
10199 	if (reset) {
10200 		start = 0;
10201 		length = charCount;
10202 	}
10203 	int[] formerRanges = getRanges(start, length);
10204 	StyleRange[] formerStyles = getStyleRanges(start, length);
10205 	int end = start + length;
10206 	final boolean wasFixedLineHeight = isFixedLineHeight();
10207 	if (start > end || start < 0) {
10208 		SWT.error(SWT.ERROR_INVALID_RANGE);
10209 	}
10210 	if (styles != null) {
10211 		if (end > charCount) {
10212 			SWT.error(SWT.ERROR_INVALID_RANGE);
10213 		}
10214 		if (ranges != null) {
10215 			if (ranges.length != styles.length << 1) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
10216 		}
10217 		int lastOffset = 0;
10218 		for (int i = 0; i < styles.length; i ++) {
10219 			if (styles[i] == null) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
10220 			int rangeStart, rangeLength;
10221 			if (ranges != null) {
10222 				rangeStart = ranges[i << 1];
10223 				rangeLength = ranges[(i << 1) + 1];
10224 			} else {
10225 				rangeStart = styles[i].start;
10226 				rangeLength = styles[i].length;
10227 			}
10228 			if (rangeLength < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
10229 			if (!(0 <= rangeStart && rangeStart + rangeLength <= charCount)) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
10230 			if (lastOffset > rangeStart) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
10231 			hasStyleWithVariableHeight |= styles[i].isVariableHeight();
10232 			lastOffset = rangeStart + rangeLength;
10233 		}
10234 	}
10235 	int rangeStart = start, rangeEnd = end;
10236 	if (styles != null && styles.length > 0) {
10237 		if (ranges != null) {
10238 			rangeStart = ranges[0];
10239 			rangeEnd = ranges[ranges.length - 2] + ranges[ranges.length - 1];
10240 		} else {
10241 			rangeStart = styles[0].start;
10242 			rangeEnd = styles[styles.length - 1].start + styles[styles.length - 1].length;
10243 		}
10244 	}
10245 
10246 	// This needs to happen before new styles are applied
10247 	int expectedBottom = 0;
10248 	if (!isFixedLineHeight() && !reset) {
10249 		int lineEnd = content.getLineAtOffset(Math.max(end, rangeEnd));
10250 		int partialTopIndex = getPartialTopIndex();
10251 		int partialBottomIndex = getPartialBottomIndex();
10252 		if (partialTopIndex <= lineEnd && lineEnd <= partialBottomIndex) {
10253 			expectedBottom = getLinePixel(lineEnd + 1);
10254 		}
10255 	}
10256 	if (reset) {
10257 		renderer.setStyleRanges(null, null);
10258 	} else {
10259 		renderer.updateRanges(start, length, length);
10260 	}
10261 	if (styles != null && styles.length > 0) {
10262 		renderer.setStyleRanges(ranges, styles);
10263 	}
10264 
10265 	// re-evaluate variable height with all styles (including new ones)
10266 	hasStyleWithVariableHeight = false;
10267 	for (StyleRange style : getStyleRanges(false)) {
10268 		hasStyleWithVariableHeight = style.isVariableHeight();
10269 		if (hasStyleWithVariableHeight) break;
10270 	}
10271 
10272 	SortedSet<Integer> modifiedLines = computeModifiedLines(formerRanges, formerStyles, ranges, styles);
10273 	resetCache(modifiedLines);
10274 	if (reset) {
10275 		super.redraw();
10276 	} else {
10277 		int lineStart = content.getLineAtOffset(Math.min(start, rangeStart));
10278 		int lineEnd = content.getLineAtOffset(Math.max(end, rangeEnd));
10279 		int partialTopIndex = getPartialTopIndex();
10280 		int partialBottomIndex = getPartialBottomIndex();
10281 		if (!(lineStart > partialBottomIndex || lineEnd < partialTopIndex)) {
10282 			int top = 0;
10283 			int bottom = clientAreaHeight;
10284 			if (partialTopIndex <= lineStart && lineStart <= partialBottomIndex) {
10285 				top = Math.max(0, getLinePixel(lineStart));
10286 			}
10287 			if (partialTopIndex <= lineEnd && lineEnd <= partialBottomIndex) {
10288 				bottom = getLinePixel(lineEnd + 1);
10289 			}
10290 			if (!(wasFixedLineHeight && isFixedLineHeight()) && bottom != expectedBottom) {
10291 				bottom = clientAreaHeight;
10292 			}
10293 			super.redraw(0, top, clientAreaWidth, bottom - top, false);
10294 		}
10295 	}
10296 	int oldColumnX = columnX;
10297 	setCaretLocation();
10298 	columnX = oldColumnX;
10299 	doMouseLinkCursor();
10300 }
10301 
10302 /**
10303  *
10304  * @param referenceRanges former ranges, sorted by order and without overlapping, typically returned {@link #getRanges(int, int)}
10305  * @param referenceStyles
10306  * @param newRanges former ranges, sorted by order and without overlapping
10307  * @param newStyles
10308  * @return
10309  */
computeModifiedLines(int[] referenceRanges, StyleRange[] referenceStyles, int[] newRanges, StyleRange[] newStyles)10310 private SortedSet<Integer> computeModifiedLines(int[] referenceRanges, StyleRange[] referenceStyles, int[] newRanges, StyleRange[] newStyles) {
10311 	if (referenceStyles == null) {
10312 		referenceStyles = new StyleRange[0];
10313 	}
10314 	if (referenceRanges == null) {
10315 		referenceRanges = createRanges(referenceStyles);
10316 	}
10317 	if (newStyles == null) {
10318 		newStyles = new StyleRange[0];
10319 	}
10320 	if (newRanges == null) {
10321 		newRanges = createRanges(newStyles);
10322 	}
10323 	if (referenceRanges.length != 2 * referenceStyles.length) {
10324 		throw new IllegalArgumentException();
10325 	}
10326 	if (newRanges.length != 2 * newStyles.length) {
10327 		throw new IllegalArgumentException();
10328 	}
10329 	SortedSet<Integer> res = new TreeSet<>();
10330 	int referenceRangeIndex = 0;
10331 	int newRangeIndex = 0;
10332 	StyleRange defaultStyle = new StyleRange();
10333 	defaultStyle.foreground = this.foreground;
10334 	defaultStyle.background = this.background;
10335 	defaultStyle.font = getFont();
10336 	int currentOffset = referenceRanges.length > 0 ? referenceRanges[0] : Integer.MAX_VALUE;
10337 	if (newRanges.length > 0) {
10338 		currentOffset = Math.min(currentOffset, newRanges[0]);
10339 	}
10340 	while (currentOffset < content.getCharCount() && (referenceRangeIndex < referenceStyles.length || newRangeIndex < newRanges.length)) {
10341 		int nextMilestoneOffset = Integer.MAX_VALUE; // next new range start/end after current offset
10342 
10343 		while (referenceRangeIndex < referenceStyles.length && endRangeOffset(referenceRanges, referenceRangeIndex) <= currentOffset) {
10344 			referenceRangeIndex++;
10345 		}
10346 		StyleRange referenceStyleAtCurrentOffset = defaultStyle;
10347 		if (isInRange(referenceRanges, referenceRangeIndex, currentOffset)) { // has styling
10348 			referenceStyleAtCurrentOffset = referenceStyles[referenceRangeIndex];
10349 			nextMilestoneOffset =  endRangeOffset(referenceRanges, referenceRangeIndex);
10350 		} else if (referenceRangeIndex < referenceStyles.length) { // no range, default styling
10351 			nextMilestoneOffset = referenceRanges[2 * referenceRangeIndex]; // beginning of next range
10352 		}
10353 
10354 		while (newRangeIndex < newStyles.length && endRangeOffset(newRanges, newRangeIndex) <= currentOffset) {
10355 			newRangeIndex++;
10356 		}
10357 		StyleRange newStyleAtCurrentOffset = defaultStyle;
10358 		if (isInRange(newRanges, newRangeIndex, currentOffset)) {
10359 			newStyleAtCurrentOffset = newStyles[newRangeIndex];
10360 			nextMilestoneOffset = Math.min(nextMilestoneOffset, endRangeOffset(newRanges, newRangeIndex));
10361 		} else if (newRangeIndex < newStyles.length) {
10362 			nextMilestoneOffset = Math.min(nextMilestoneOffset, newRanges[2 * newRangeIndex]);
10363 		}
10364 
10365 		if (!referenceStyleAtCurrentOffset.similarTo(newStyleAtCurrentOffset)) {
10366 			int fromLine = getLineAtOffset(currentOffset);
10367 			int toLine = getLineAtOffset(nextMilestoneOffset - 1);
10368 			for (int line = fromLine; line <= toLine; line++) {
10369 				res.add(line);
10370 			}
10371 			currentOffset = toLine + 1 < getLineCount() ? getOffsetAtLine(toLine + 1) : content.getCharCount();
10372 		} else {
10373 			currentOffset = nextMilestoneOffset;
10374 		}
10375 	}
10376 	return res;
10377 }
10378 private int[] createRanges(StyleRange[] referenceStyles) {
10379 	int[] referenceRanges;
10380 	referenceRanges = new int[2 * referenceStyles.length];
10381 	for (int i = 0; i < referenceStyles.length; i++) {
10382 		referenceRanges[2 * i] = referenceStyles[i].start;
10383 		referenceRanges[2 * i + 1] = referenceStyles[i].length;
10384 	}
10385 	return referenceRanges;
10386 }
10387 
10388 private boolean isInRange(int[] ranges, int styleIndex, int offset) {
10389 	if (ranges == null || ranges.length == 0 || styleIndex < 0 || 2 * styleIndex + 1 > ranges.length) {
10390 		return false;
10391 	}
10392 	int start = ranges[2 * styleIndex];
10393 	int length = ranges[2 * styleIndex + 1];
10394 	return offset >= start && offset < start + length;
10395 }
10396 
10397 /**
10398  * The offset on which the range ends (excluded)
10399  * @param ranges
10400  * @param styleIndex
10401  * @return
10402  */
10403 private int endRangeOffset(int[] ranges, int styleIndex) {
10404 	if (styleIndex < 0 || 2 * styleIndex > ranges.length) {
10405 		throw new IllegalArgumentException();
10406 	}
10407 	int start = ranges[2 * styleIndex];
10408 	int length = ranges[2 * styleIndex + 1];
10409 	return start + length;
10410 }
10411 
10412 /**
10413  * Sets styles to be used for rendering the widget content. All styles
10414  * in the widget will be replaced with the given set of styles.
10415  * <p>
10416  * Note: Because a StyleRange includes the start and length, the
10417  * same instance cannot occur multiple times in the array of styles.
10418  * If the same style attributes, such as font and color, occur in
10419  * multiple StyleRanges, <code>setStyleRanges(int[], StyleRange[])</code>
10420  * can be used to share styles and reduce memory usage.
10421  * </p><p>
10422  * Should not be called if a LineStyleListener has been set since the
10423  * listener maintains the styles.
10424  * </p>
10425  *
10426  * @param ranges StyleRange objects containing the style information.
10427  * The ranges should not overlap. The style rendering is undefined if
10428  * the ranges do overlap. Must not be null. The styles need to be in order.
10429  * @exception SWTException <ul>
10430  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10431  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10432  * </ul>
10433  * @exception IllegalArgumentException <ul>
10434  *    <li>ERROR_NULL_ARGUMENT when the list of ranges is null</li>
10435  *    <li>ERROR_INVALID_RANGE when the last of the style ranges is outside the valid range (&gt; getCharCount())</li>
10436  * </ul>
10437  *
10438  * @see #setStyleRanges(int[], StyleRange[])
10439  */
10440 public void setStyleRanges(StyleRange[] ranges) {
10441 	checkWidget();
10442 	if (isListening(ST.LineGetStyle)) return;
10443 	if (ranges == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
10444 	setStyleRanges(0, 0, null, ranges, true);
10445 }
10446 /**
10447  * Sets the tab width.
10448  *
10449  * @param tabs tab width measured in characters.
10450  * @exception SWTException <ul>
10451  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10452  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10453  * </ul>
10454  *
10455  * @see #setTabStops(int[])
10456  */
10457 public void setTabs(int tabs) {
10458 	checkWidget();
10459 	tabLength = tabs;
10460 	renderer.setFont(null, tabs);
10461 	resetCache(0, content.getLineCount());
10462 	setCaretLocation();
10463 	super.redraw();
10464 }
10465 
10466 /**
10467  * Sets the receiver's tab list. Each value in the tab list specifies
10468  * the space in points from the origin of the document to the respective
10469  * tab stop.  The last tab stop width is repeated continuously.
10470  *
10471  * @param tabs the new tab list (or null)
10472  *
10473  * @exception SWTException <ul>
10474  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10475  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10476  * </ul>
10477  * @exception IllegalArgumentException <ul>
10478  *    <li>ERROR_INVALID_ARGUMENT - if a tab stop is negative or less than the previous stop in the list</li>
10479  * </ul>
10480  *
10481  * @see StyledText#getTabStops()
10482  *
10483  * @since 3.6
10484  */
10485 public void setTabStops(int [] tabs) {
10486 	checkWidget();
10487 	if (tabs != null) {
10488 		int pos = 0;
10489 		int[] newTabs = new int[tabs.length];
10490 		for (int i = 0; i < tabs.length; i++) {
10491 			if (tabs[i] < pos) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
10492 			newTabs[i] = pos = tabs[i];
10493 		}
10494 		this.tabs = newTabs;
10495 	} else {
10496 		this.tabs = null;
10497 	}
10498 	resetCache(0, content.getLineCount());
10499 	setCaretLocation();
10500 	super.redraw();
10501 }
10502 
10503 /**
10504  * Sets the widget content.
10505  * If the widget has the SWT.SINGLE style and "text" contains more than
10506  * one line, only the first line is rendered but the text is stored
10507  * unchanged. A subsequent call to getText will return the same text
10508  * that was set.
10509  * <p>
10510  * <b>Note:</b> Only a single line of text should be set when the SWT.SINGLE
10511  * style is used.
10512  * </p>
10513  *
10514  * @param text new widget content. Replaces existing content. Line styles
10515  * 	that were set using StyledText API are discarded.  The
10516  * 	current selection is also discarded.
10517  * @exception SWTException <ul>
10518  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10519  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10520  * </ul>
10521  * @exception IllegalArgumentException <ul>
10522  *    <li>ERROR_NULL_ARGUMENT when string is null</li>
10523  * </ul>
10524  */
10525 public void setText(String text) {
10526 	checkWidget();
10527 	if (text == null) {
10528 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
10529 	}
10530 	Event event = new Event();
10531 	event.start = 0;
10532 	event.end = getCharCount();
10533 	event.text = text;
10534 	event.doit = true;
10535 	notifyListeners(SWT.Verify, event);
10536 	if (event.doit) {
10537 		StyledTextEvent styledTextEvent = null;
10538 		if (isListening(ST.ExtendedModify)) {
10539 			styledTextEvent = new StyledTextEvent(content);
10540 			styledTextEvent.start = event.start;
10541 			styledTextEvent.end = event.start + event.text.length();
10542 			styledTextEvent.text = content.getTextRange(event.start, event.end - event.start);
10543 		}
10544 		content.setText(event.text);
10545 		notifyListeners(SWT.Modify, event);
10546 		if (styledTextEvent != null) {
10547 			notifyListeners(ST.ExtendedModify, styledTextEvent);
10548 		}
10549 	}
10550 }
10551 
10552 /**
10553  * Sets the base text direction (a.k.a. "paragraph direction") of the receiver,
10554  * which must be one of the constants <code>SWT.LEFT_TO_RIGHT</code> or
10555  * <code>SWT.RIGHT_TO_LEFT</code>.
10556  * <p>
10557  * <code>setOrientation</code> would override this value with the text direction
10558  * that is consistent with the new orientation.
10559  * </p>
10560  * <p>
10561  * <b>Warning</b>: This API is currently only implemented on Windows.
10562  * It doesn't set the base text direction on GTK and Cocoa.
10563  * </p>
10564  *
10565  * @param textDirection the base text direction style
10566  *
10567  * @exception SWTException <ul>
10568  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10569  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10570  * </ul>
10571  *
10572  * @see SWT#FLIP_TEXT_DIRECTION
10573  */
10574 @Override
10575 public void setTextDirection(int textDirection) {
10576 	checkWidget();
10577 	int oldStyle = getStyle();
10578 	super.setTextDirection(textDirection);
10579 	if (isAutoDirection () || oldStyle != getStyle()) {
10580 		resetBidiData();
10581 	}
10582 }
10583 
10584 /**
10585  * Sets the text limit to the specified number of characters.
10586  * <p>
10587  * The text limit specifies the amount of text that
10588  * the user can type into the widget.
10589  * </p>
10590  *
10591  * @param limit the new text limit.
10592  * @exception SWTException <ul>
10593  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10594  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10595  * </ul>
10596  * @exception IllegalArgumentException <ul>
10597  *   <li>ERROR_CANNOT_BE_ZERO when limit is 0</li>
10598  * </ul>
10599  */
10600 public void setTextLimit(int limit) {
10601 	checkWidget();
10602 	if (limit == 0) {
10603 		SWT.error(SWT.ERROR_CANNOT_BE_ZERO);
10604 	}
10605 	textLimit = limit;
10606 }
10607 /**
10608  * Sets the top index. Do nothing if there is no text set.
10609  * <p>
10610  * The top index is the index of the line that is currently at the top
10611  * of the widget. The top index changes when the widget is scrolled.
10612  * Indexing starts from zero.
10613  * Note: The top index is reset to 0 when new text is set in the widget.
10614  * </p>
10615  *
10616  * @param topIndex new top index. Must be between 0 and
10617  * 	getLineCount() - fully visible lines per page. If no lines are fully
10618  * 	visible the maximum value is getLineCount() - 1. An out of range
10619  * 	index will be adjusted accordingly.
10620  * @exception SWTException <ul>
10621  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10622  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10623  * </ul>
10624  */
10625 public void setTopIndex(int topIndex) {
10626 	checkWidget();
10627 	if (getCharCount() == 0) {
10628 		return;
10629 	}
10630 	int lineCount = content.getLineCount(), pixel;
10631 	if (isFixedLineHeight()) {
10632 		int pageSize = Math.max(1, Math.min(lineCount, getLineCountWhole()));
10633 		if (topIndex < 0) {
10634 			topIndex = 0;
10635 		} else if (topIndex > lineCount - pageSize) {
10636 			topIndex = lineCount - pageSize;
10637 		}
10638 		pixel = getLinePixel(topIndex);
10639 	} else {
10640 		topIndex = Math.max(0, Math.min(lineCount - 1, topIndex));
10641 		pixel = getLinePixel(topIndex);
10642 		if (pixel > 0) {
10643 			pixel = getAvailableHeightBellow(pixel);
10644 		} else {
10645 			pixel = getAvailableHeightAbove(pixel);
10646 		}
10647 	}
10648 	scrollVertical(pixel, true);
10649 }
10650 /**
10651  * Sets the top margin.
10652  *
10653  * @param topMargin the top margin.
10654  * @exception SWTException <ul>
10655  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10656  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10657  * </ul>
10658  *
10659  * @since 3.5
10660  */
10661 public void setTopMargin (int topMargin) {
10662 	checkWidget();
10663 	setMargins(getLeftMargin(), topMargin, rightMargin, bottomMargin);
10664 }
10665 /**
10666  * Sets the top SWT logical point offset. Do nothing if there is no text set.
10667  * <p>
10668  * The top point offset is the vertical SWT logical point offset of the widget. The
10669  * widget is scrolled so that the given SWT logical point position is at the top.
10670  * The top index is adjusted to the corresponding top line.
10671  * Note: The top point is reset to 0 when new text is set in the widget.
10672  * </p>
10673  *
10674  * @param pixel new top point offset. Must be between 0 and
10675  * 	(getLineCount() - visible lines per page) / getLineHeight()). An out
10676  * 	of range offset will be adjusted accordingly.
10677  * @exception SWTException <ul>
10678  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10679  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10680  * </ul>
10681  * @since 2.0
10682  */
10683 public void setTopPixel(int pixel) {
10684 	checkWidget();
10685 	if (getCharCount() == 0) {
10686 		return;
10687 	}
10688 	if (pixel < 0) pixel = 0;
10689 	int lineCount = content.getLineCount();
10690 	int height = clientAreaHeight - topMargin - bottomMargin;
10691 	int verticalOffset = getVerticalScrollOffset();
10692 	if (isFixedLineHeight()) {
10693 		int maxTopPixel = Math.max(0, lineCount * getVerticalIncrement() - height);
10694 		if (pixel > maxTopPixel) pixel = maxTopPixel;
10695 		pixel -= verticalOffset;
10696 	} else {
10697 		pixel -= verticalOffset;
10698 		if (pixel > 0) {
10699 			pixel = getAvailableHeightBellow(pixel);
10700 		}
10701 	}
10702 	scrollVertical(pixel, true);
10703 }
10704 /**
10705  * Sets whether the widget wraps lines.
10706  * <p>
10707  * This overrides the creation style bit SWT.WRAP.
10708  * </p>
10709  *
10710  * @param wrap true=widget wraps lines, false=widget does not wrap lines
10711  * @since 2.0
10712  */
10713 public void setWordWrap(boolean wrap) {
10714 	checkWidget();
10715 	if ((getStyle() & SWT.SINGLE) != 0) return;
10716 	if (wordWrap == wrap) return;
10717 	if (wordWrap && blockSelection) setBlockSelection(false);
10718 	wordWrap = wrap;
10719 	resetCache(0, content.getLineCount());
10720 	horizontalScrollOffset = 0;
10721 	ScrollBar horizontalBar = getHorizontalBar();
10722 	if (horizontalBar != null) {
10723 		horizontalBar.setVisible(!wordWrap);
10724 	}
10725 	setScrollBars(true);
10726 	setCaretLocation();
10727 	super.redraw();
10728 }
10729 /**
10730  * Sets the wrap line indentation of the widget.
10731  * <p>
10732  * It is the amount of blank space, in points, at the beginning of each wrapped line.
10733  * When a line wraps in several lines all the lines but the first one is indented
10734  * by this amount.
10735  * </p>
10736  *
10737  * @param wrapIndent the new wrap indent
10738  *
10739  * @exception SWTException <ul>
10740  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10741  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10742  * </ul>
10743  *
10744  * @see #setLineWrapIndent(int, int, int)
10745  *
10746  * @since 3.6
10747  */
10748 public void setWrapIndent(int wrapIndent) {
10749 	checkWidget();
10750 	if (this.wrapIndent == wrapIndent || wrapIndent < 0) return;
10751 	this.wrapIndent = wrapIndent;
10752 	resetCache(0, content.getLineCount());
10753 	setCaretLocation();
10754 	super.redraw();
10755 }
10756 boolean showLocation(Rectangle rect, boolean scrollPage) {
10757 	boolean scrolled = false;
10758 	if (rect.y < topMargin) {
10759 		scrolled = scrollVertical(rect.y - topMargin, true);
10760 	} else if (rect.y + rect.height > clientAreaHeight - bottomMargin) {
10761 		if (clientAreaHeight - topMargin - bottomMargin <= 0) {
10762 			scrolled = scrollVertical(rect.y - topMargin, true);
10763 		} else {
10764 			scrolled = scrollVertical(rect.y + rect.height - (clientAreaHeight - bottomMargin), true);
10765 		}
10766 	}
10767 	int width = clientAreaWidth - rightMargin - leftMargin;
10768 	if (width > 0) {
10769 		int minScroll = scrollPage ? width / 4 : 0;
10770 		if (rect.x < leftMargin) {
10771 			int scrollWidth = Math.max(leftMargin - rect.x, minScroll);
10772 			int maxScroll = horizontalScrollOffset;
10773 			scrolled = scrollHorizontal(-Math.min(maxScroll, scrollWidth), true);
10774 		} else if (rect.x + rect.width > (clientAreaWidth - rightMargin)) {
10775 			int scrollWidth =  Math.max(rect.x + rect.width - (clientAreaWidth - rightMargin), minScroll);
10776 			int maxScroll = renderer.getWidth() - horizontalScrollOffset - clientAreaWidth;
10777 			scrolled = scrollHorizontal(Math.min(maxScroll, scrollWidth), true);
10778 		}
10779 	}
10780 	return scrolled;
10781 }
10782 /**
10783  * Sets the caret location and scrolls the caret offset into view.
10784  */
10785 void showCaret() {
10786 	Rectangle bounds = getBoundsAtOffset(caretOffset);
10787 	if (!showLocation(bounds, true)) {
10788 		setCaretLocation();
10789 	}
10790 }
10791 /**
10792  * Scrolls the selection into view.
10793  * <p>
10794  * The end of the selection will be scrolled into view.
10795  * Note that if a right-to-left selection exists, the end of the selection is
10796  * the visual beginning of the selection (i.e., where the caret is located).
10797  * </p>
10798  *
10799  * @exception SWTException <ul>
10800  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
10801  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
10802  * </ul>
10803  */
10804 public void showSelection() {
10805 	checkWidget();
10806 	// is selection from right-to-left?
10807 	boolean rightToLeft = caretOffset == selection.x;
10808 	int startOffset, endOffset;
10809 	if (rightToLeft) {
10810 		startOffset = selection.y;
10811 		endOffset = selection.x;
10812 	} else {
10813 		startOffset = selection.x;
10814 		endOffset = selection.y;
10815 	}
10816 
10817 	Rectangle startBounds = getBoundsAtOffset(startOffset);
10818 	Rectangle endBounds = getBoundsAtOffset(endOffset);
10819 
10820 	// can the selection be fully displayed within the widget's visible width?
10821 	int w = clientAreaWidth - leftMargin - rightMargin;
10822 	boolean selectionFits = rightToLeft ? startBounds.x - endBounds.x <= w : endBounds.x - startBounds.x <= w;
10823 	if (selectionFits) {
10824 		// show as much of the selection as possible by first showing
10825 		// the start of the selection
10826 		if (showLocation(startBounds, false)) {
10827 			// endX value could change if showing startX caused a scroll to occur
10828 			endBounds = getBoundsAtOffset(endOffset);
10829 		}
10830 		// the character at endOffset is not part of the selection
10831 		endBounds.width = endOffset == caretOffset ? getCaretWidth() : 0;
10832 		showLocation(endBounds, false);
10833 	} else {
10834 		// just show the end of the selection since the selection start
10835 		// will not be visible
10836 		showLocation(endBounds, true);
10837 	}
10838 }
10839 void updateCaretVisibility() {
10840 	Caret caret = getCaret();
10841 	if (caret != null) {
10842 		if (blockSelection && blockXLocation != -1) {
10843 			caret.setVisible(false);
10844 		} else {
10845 			Point location = caret.getLocation();
10846 			Point size = caret.getSize();
10847 			boolean visible =
10848 				topMargin <= location.y + size.y && location.y <= clientAreaHeight - bottomMargin &&
10849 				leftMargin <= location.x + size.x && location.x <= clientAreaWidth - rightMargin;
10850 			caret.setVisible(visible);
10851 		}
10852 	}
10853 }
10854 /**
10855  * Updates the selection and caret position depending on the text change.
10856  * <p>
10857  * If the selection intersects with the replaced text, the selection is
10858  * reset and the caret moved to the end of the new text.
10859  * If the selection is behind the replaced text it is moved so that the
10860  * same text remains selected.  If the selection is before the replaced text
10861  * it is left unchanged.
10862  * </p>
10863  *
10864  * @param startOffset offset of the text change
10865  * @param replacedLength length of text being replaced
10866  * @param newLength length of new text
10867  */
10868 void updateSelection(int startOffset, int replacedLength, int newLength) {
10869 	if (selection.y <= startOffset) {
10870 		// selection ends before text change
10871 		if (isWordWrap()) setCaretLocation();
10872 		return;
10873 	}
10874 	if (selection.x < startOffset) {
10875 		// clear selection fragment before text change
10876 		internalRedrawRange(selection.x, startOffset - selection.x);
10877 	}
10878 	if (selection.y > startOffset + replacedLength && selection.x < startOffset + replacedLength) {
10879 		// clear selection fragment after text change.
10880 		// do this only when the selection is actually affected by the
10881 		// change. Selection is only affected if it intersects the change (1GDY217).
10882 		int netNewLength = newLength - replacedLength;
10883 		int redrawStart = startOffset + newLength;
10884 		internalRedrawRange(redrawStart, selection.y + netNewLength - redrawStart);
10885 	}
10886 	if (selection.y > startOffset && selection.x < startOffset + replacedLength) {
10887 		// selection intersects replaced text. set caret behind text change
10888 		setSelection(startOffset + newLength, 0, true, false);
10889 	} else {
10890 		// move selection to keep same text selected
10891 		setSelection(selection.x + newLength - replacedLength, selection.y - selection.x, true, false);
10892 	}
10893 	setCaretLocation();
10894 }
10895 }
10896